package com.vhall.component.service.room.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.poi.excel.ExcelReader;
import cn.hutool.poi.excel.ExcelUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.base.Preconditions;
import com.google.common.base.Splitter;
import com.google.common.collect.Maps;
import com.vhall.component.dao.account.AccountMapper;
import com.vhall.component.dao.data.AccountForDataMapper;
import com.vhall.component.dao.livecolumn.LiveColumnMapper;
import com.vhall.component.dao.pendant.PendantMappingMapper;
import com.vhall.component.dao.room.*;
import com.vhall.component.entity.account.AccountEntity;
import com.vhall.component.entity.audit.dto.AuditRecordListDTO;
import com.vhall.component.entity.audit.dto.CreateAuditRecordDTO;
import com.vhall.component.entity.audit.vo.AuditRecordResponseVO;
import com.vhall.component.entity.data.vo.UserBasicStatisticsRspVO;
import com.vhall.component.entity.data.vo.UserParamVO;
import com.vhall.component.entity.goods.vo.SearchGoodsResponseVO;
import com.vhall.component.entity.inav.vo.InavRoomGetRespVO;
import com.vhall.component.entity.inav.vo.SelectVideoData;
import com.vhall.component.entity.inav.vo.TagVO;
import com.vhall.component.entity.livecolumn.entity.LiveColumnEntity;
import com.vhall.component.entity.page.vo.PageVO;
import com.vhall.component.entity.pendant.PendantMappingEntity;
import com.vhall.component.entity.pendant.vo.PendantListReqVO;
import com.vhall.component.entity.pendant.vo.PendantRspVO;
import com.vhall.component.entity.record.RecordEntity;
import com.vhall.component.entity.room.RoomBaseInfoResponseVO;
import com.vhall.component.entity.room.RoomConfig;
import com.vhall.component.entity.room.dto.*;
import com.vhall.component.entity.room.entity.*;
import com.vhall.component.entity.room.enums.*;
import com.vhall.component.entity.room.vo.*;
import com.vhall.component.entity.subject.enums.StatusEnum;
import com.vhall.component.entity.tag.PageChannelEntity;
import com.vhall.component.entity.tag.TagEntity;
import com.vhall.component.entity.tag.vo.PageChannelRspVO;
import com.vhall.component.entity.thirduser.ThirdUserVO;
import com.vhall.component.framework.common.constant.CommonConstant;
import com.vhall.component.framework.common.exception.BusinessException;
import com.vhall.component.framework.common.utils.CollectionUtils;
import com.vhall.component.plugin.common.constant.ConstStr;
import com.vhall.component.plugin.common.constant.HttpConstant;
import com.vhall.component.plugin.common.constant.PaasCommonConstant;
import com.vhall.component.plugin.common.constant.RedisKey;
import com.vhall.component.plugin.common.enums.DeleteStatusEnum;
import com.vhall.component.plugin.common.enums.RoomConfigStatusEnum;
import com.vhall.component.plugin.common.exception.BizErrorCode;
import com.vhall.component.plugin.common.support.Token;
import com.vhall.component.plugin.common.support.TokenThreadLocal;
import com.vhall.component.plugin.common.utils.BeanCopyUtil;
import com.vhall.component.plugin.common.utils.JsonUtil;
import com.vhall.component.plugin.common.utils.RandomUtil;
import com.vhall.component.plugin.common.utils.RedisLocker;
import com.vhall.component.plugin.core.service.GuoXinService;
import com.vhall.component.plugin.core.service.OssService;
import com.vhall.component.service.audit.AuditRecordService;
import com.vhall.component.service.common.BaseCommonService;
import com.vhall.component.service.goods.GuoxinPrdService;
import com.vhall.component.service.pendant.PendantConsoleService;
import com.vhall.component.service.pendant.common.PendantConstant;
import com.vhall.component.service.room.*;
import com.vhall.component.service.room.concant.RoomConstant;
import com.vhall.component.service.room.utils.QrCodeUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Base64Utils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.text.MessageFormat;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.util.*;
import java.util.stream.Collectors;

import static com.vhall.component.plugin.common.constant.RedisKey.*;

/**
 * @author yongzheng.xu
 * @date 2021-06-08 14:40
 */
@Slf4j
@Service
public class RoomConsoleServiceImpl extends RoomBaseService implements RoomConsoleService {



    @Value("${paas.apps.lite.appId}")
    public String appId;


    @Autowired
    private SupplyMapper supplyMapper;

    @Autowired
    private RoomJoinsMapper roomJoinsMapper;

    /**
     * 现在的逻辑是paas只调用这一次的回调，然后在这个回调中去生成回放，这样会导致record服务和room服务发生循环依赖。
     * 所以在这里直接操作record表
     */
    @Autowired
    private RecordOfRoomMapper recordOfRoomMapper;

    /**
     * 为了防止循环依赖，在这里直接访问tag。
     */
    @Autowired
    private TagOfRoomMapper tagOfRoomMapper;

    @Autowired
    private OssService ossService;

    @Autowired
    private RoomCacheService roomCacheService;

    @Autowired
    private PushStreamMapper pushStreamMapper;

    @Autowired
    private RoomInnerService roomInnerService;

    @Autowired
    private PendantMappingMapper pendantMappingMapper;

    @Autowired
    private PendantConsoleService pendantConsoleService;

    @Autowired
    private AccountMapper accountMapper;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private AccountForDataMapper accountForDataMapper;
    @Resource
    private AuditRecordService auditRecordService;
    @Resource
    private InvestRefService investRefService;
    @Resource
    private InvestRefRelationService investRefRelationService;
    private RoomBaseInfoResponseVO roomBaseInfoResponseVO = null;

    @Resource
    private RoomQrCodeMapper roomQrCodeMapper;

    @Resource
    private QrCodeUtils qrCodeUtils;
    @Resource
    private AnchorRoomRelationService anchorRoomRelationService;
    @Resource
    private RoomService roomService;
    @Resource
    private RoomDraftRecordService roomDraftRecordService;

    @Value("${guoxin.qr.url:''}")
    private String qrDefaultUrl;

    @Resource
    private LiveColumnMapper liveColumnMapper;

    @Value("${spring.profiles.active:''}")
    private String active;

    private final static Integer MAX_PUSH = 10;

    @Resource
    private GuoxinPrdService guoxinPrdService;

    @Resource
    private BaseCommonService baseCommonService;
    @Value("${h5.redirect.url}")
    private String h5Url;
    @Autowired
    private RoomColumnPushMsgThirdMapper roomColumnPushMsgThirdMapper;

    @Resource
    private RoomProdRelationService roomProdRelationService;
    @Resource
    private RoomWhiteFileRelationService whiteFileRelationService;
    @Resource
    private RoomWhitePhoneRelationService whitePhoneRelationService;
    /**
     * 可导入文件类型
     */
    private static final List<String> EXT_NAME_LIST = Arrays.asList("xlsx", "xls");

    @Resource
    private GuoXinService guoXinService;

    @Value("${vhall.viewingPath}")
    public String viewingPath;

    /**
     * console 端
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public RoomRespVO create(CreateRoomNewVO reqVO) {
        return createRoom(reqVO, reqVO.getImageUrl());
    }

    /**
     * app 调用而来
     */
    @Override
    public RoomRespVO create(CreateRoomVO reqVO, String imageBase64) {
        String url = coverImageUpload(imageBase64);
        return createRoom(reqVO, url);
    }


    private RoomRespVO createRoom(CreateRoomVO reqVO, String coverUrl) {

        basicValidateVo(reqVO);

        HashMap<String, Object> params = buildCommonParam();
        String liveRoom = paasService.createLiveRoom(params, "/api/v2/room/create", com.vhall.component.plugin.common.constant.RoomConstant.INT_FACE1);
        String hdRoom = paasService.createLiveRoom(params, "/api/v2/inav/create", com.vhall.component.plugin.common.constant.RoomConstant.INT_FACE2);
        String channelRoom = paasService.createLiveRoom(params, "/api/v2/channel/create", com.vhall.component.plugin.common.constant.RoomConstant.INT_FACE3);
        String nifyChannelRoom = paasService.createLiveRoom(params, "/api/v2/channel/create", com.vhall.component.plugin.common.constant.RoomConstant.INT_FACE3);
        //设置聊天限频
        final Boolean result = paasService.setChannelLimit(params, channelRoom);
        //设置房间表情限流
        Boolean giftResult = paasService.setGiftLimit(params, channelRoom, "/api/v2/channel/set-channel-limit");
        if (StringUtils.isBlank(liveRoom) || StringUtils.isBlank(hdRoom)
                || StringUtils.isBlank(channelRoom)
                || StringUtils.isBlank(nifyChannelRoom) || !result || !giftResult) {
            throw new BusinessException(BizErrorCode.BIZ_CREATE_FAILED);
        }

        Token info = TokenThreadLocal.getToken();
        RoomsEntity room = buildRoom(reqVO, liveRoom, hdRoom, channelRoom, nifyChannelRoom, info);
        //默认房间为待送审
        if (reqVO.getLiveRormal() == 0) {
//            测试直播默认设置为通过审核
            room.setAuditStatus(RoomAuditStatusEnum.APPROVED.getAuditStatus());
            room.setAuditNote("测试直播默认设置为通过审核");
            room.setReviewer(info.getAccountId());
        } else {
            room.setAuditStatus(RoomAuditStatusEnum.SUBMIT_FOR_APPROVAL.getAuditStatus());
        }
        room.setCoverImage(coverUrl);

        switch (Objects.requireNonNull(LiveRoomTypeEnum.parse(reqVO.getLiveType()))) {
            case ON_DEMAND:
                room.setPosterUrl(coverUrl);
                break;
            default:
                break;
        }

        // 设置房间默认水印模板id
        String defaultWatermarkTemplateId = roomInnerService.getDefaultWatermarkTemplateId();
        room.setWatermarkTempId(Integer.parseInt(defaultWatermarkTemplateId));

        roomMapper.insert(room);

        openChannelSwitch(room.getChannelId(), room.getAccountId());
        setChannelSwitchOptions(room.getChannelId(), room.getAccountId());

//        创建房间后处理
        handleCreateRoomAfter(reqVO, info, room, defaultWatermarkTemplateId);
//        设置返回值
        RoomRespVO vo = new RoomRespVO();
        vo.setIlId(room.getIlId());
        vo.setRoomId(room.getRoomId());
        vo.setSubject(room.getSubject());
        return vo;
    }

    public void setChannelSwitchOptions(String channelId, Integer accountId) {
        Map<String, Object> map = new HashMap<>(8);
//        app_id: fGWLbDnj
//        client: pc_browser
//        vss_token: access:fGWLbDnj:7263fa4c753af99d
//        channel_id: ch_6Euv24U3
//        third_party_user_id: 15197
//        switch: 2
//        switch_options: 2
//        from: js
//        token: 7b5f9a882f427471
        map.put("switch", 2);
        map.put(PaasCommonConstant.CHANNEL_ID_KEY, channelId);
        map.put(PaasCommonConstant.THIRD_PARTY_USER_ID_KEY, accountId);
        map.put(PaasCommonConstant.CLIENT_KEY, "pc_browser");
        map.put("switch_options", 2);
        paasService.setChannelSwitchOptions(map);
    }

    private void openChannelSwitch(String channel, Integer uid) {
        Map<String, Object> map = new HashMap<>();
        map.put("switch", 2);
        map.put(PaasCommonConstant.CHANNEL_ID_KEY, channel);
        paasService.setChannelSwitch(map);
    }

    /**
     * 创建房间后处理
     *
     * @param reqVO
     * @param info
     * @param room
     * @param defaultWatermarkTemplateId
     */
    private void handleCreateRoomAfter(CreateRoomVO reqVO, Token info, RoomsEntity room, String defaultWatermarkTemplateId) {
        //批量插入房间功能开关状态表
        batchInsertRoomConfig(room);
//        直播录制/假直播 相关业务处理
        handleFakeLiveBroadcast(room);

        // 点播类型的直播，如果有挂件，要进行关联
        associatedPendant(room, reqVO);

//      创建直播间信息补充 数据
        inserRoomSupply(room);
//        创建参会成员表
        inserRoomJoins(info, room);
        //设置水印
        setWatermark(room.getRoomId(), Integer.parseInt(defaultWatermarkTemplateId));
        // 主播账号信息
        relationRoomToAnchor(reqVO, room);
        // 投资依据关联信息
        relationRoomToInvestRef(reqVO, room);
//      关联国信的私募产品信息
        relationRoomToGuoxinProd(reqVO, room);
        //清理草稿
        clearRoomDraftRecord(reqVO);
//        关联客户可见数据逻辑的处理
        relationRoomToLimitUserScopeInfo(reqVO,room);
//        暖场信息构建
        buildWarmInfo(room);
        // 缓存参会信息
        roomCacheService.cacheRoomJoins(room.getRoomId(), info.getAccountId());
    }

    /**
     * 关联客户可见数据逻辑的处理
     * @param reqVO
     * @param room
     */
    private void relationRoomToLimitUserScopeInfo(CreateRoomVO reqVO, RoomsEntity room) {
//        关联房间和白名单手机号以及相关上传文件
        if (Objects.equals(reqVO.getLimitUserScope(), LiveLimitUserScopeEnum.WHITELIST_USER_VISIBLE.getValue())) {
            relationWhitePhoneFileToRoom(reqVO,room);
        }else if(Objects.equals(reqVO.getLimitUserScope(), LiveLimitUserScopeEnum.HOLDING_PRODUCT_USER_VISIBLE.getValue())){
            relationLimitUserScopeProdToRoom(reqVO,room);
        }
    }

    /**
     * 关联房间和持仓产品
     * @param reqVO
     * @param room
     */
    private void relationLimitUserScopeProdToRoom(CreateRoomVO reqVO, RoomsEntity room) {
        List<String> visibleProdCodes = reqVO.getVisibleProdCodes();
        Integer roomDraftRecordId = reqVO.getRoomDraftRecordId();
        String roomId = room.getRoomId();
        Integer ilId = room.getIlId();
        roomProdRelationService.relationRoomToGuoxinProd(visibleProdCodes, roomDraftRecordId, roomId, ilId, RoomAssociationTypeEnum.PURCHASE_ASSOCIATION.getValue());
    }

    /**
     * 关联房间和白名单手机号和白名单文件
     * @param reqVO
     * @param room
     */
    private void relationWhitePhoneFileToRoom(CreateRoomVO reqVO, RoomsEntity room) {
        Integer whiteFileId = reqVO.getWhiteFileId();
        Integer ilId = room.getIlId();
        Integer roomDraftRecordId = reqVO.getRoomDraftRecordId();
        clearRoomRelationWhiteInfo(whiteFileId, ilId);
        RoomWhiteFileRelationEntity whiteFileRelationEntity = whiteFileRelationService.getById(whiteFileId);
        if (Objects.nonNull(whiteFileRelationEntity)) {
            whiteFileRelationEntity.setIlId(ilId);
            whiteFileRelationEntity.setRoomDraftRecordId(roomDraftRecordId);
            whiteFileRelationService.updateById(whiteFileRelationEntity);
            whitePhoneRelationService.batchChangeRoomWhitePhoneRelationInfo(whiteFileId, ilId, roomDraftRecordId);
        }else{
            throw new BusinessException(BizErrorCode.BIZ_ROOM_RELATION_WHITE_FILE_EMPTY);
        }
    }

    /**
     * 清理房间与白名单文件和白名单手机号的关联
     * @param whiteFileId 白名单文件 id
     * @param ilId 房间id
     */
    private void clearRoomRelationWhiteInfo(Integer whiteFileId, Integer ilId) {
        LambdaQueryWrapper<RoomWhiteFileRelationEntity> fileRelationEntityLambdaQueryWrapper = Wrappers.lambdaQuery();
        fileRelationEntityLambdaQueryWrapper.eq(RoomWhiteFileRelationEntity::getIlId, ilId).last(" limit 1");
        RoomWhiteFileRelationEntity oldWhiteFile = whiteFileRelationService.getOne(fileRelationEntityLambdaQueryWrapper);
        if (Objects.nonNull(oldWhiteFile)) {
            if (!Objects.equals(whiteFileId,oldWhiteFile.getId())) {
                oldWhiteFile.setIlId(0);
                oldWhiteFile.setRoomDraftRecordId(0);
                whiteFileRelationService.updateById(oldWhiteFile);
                whitePhoneRelationService.batchChangeRoomWhitePhoneRelationInfo(whiteFileId, 0, 0);
            }
        }
    }



    /**
     * 创建房间的适当性豁免房间关联
     * @param reqVO
     * @param room
     */
    private void relationRoomToGuoxinProd(CreateRoomVO reqVO, RoomsEntity room) {
//        适当性豁免产品的编号产品编号列表
        List<String> exemptProdCodes = reqVO.getExemptProdCodes();
        Integer roomDraftRecordId = reqVO.getRoomDraftRecordId();
        String roomId = room.getRoomId();
        Integer ilId = room.getIlId();
        roomProdRelationService.relationRoomToGuoxinProd(exemptProdCodes, roomDraftRecordId, roomId, ilId, RoomAssociationTypeEnum.EXEMPTION_ASSOCIATION.getValue());
    }



    /**
     * 直播录制/假直播 相关业务处理
     *
     * @param room
     */
    private void handleFakeLiveBroadcast(RoomsEntity room) {
        // 录播视频加入缓存，给定时调度使用 ;开启了审核，就是待审核状态；关闭审核，就是审核通过状态
        int auditStatus = getRoomAuditStatus(room);
        addRecordedVideoToCache(room, auditStatus);
        //录制直播-关联到音视频管理
        recordVideoRelevanceRoom(room);
    }

    private void recordVideoRelevanceRoom(RoomsEntity room) {
        if (room.getLiveType().equals(LiveRoomTypeEnum.FAKE_LIVE_BROADCAST.getType())) {
            String demandVodId = room.getDemandVodId();
            RecordEntity byVodId = recordOfRoomMapper.findByVodId(demandVodId);
            byVodId.setRoomId(room.getRoomId());
            byVodId.setIlId(room.getIlId());
            recordOfRoomMapper.updateById(byVodId);
        }
    }

    private void clearRoomDraftRecord(CreateRoomVO reqVO) {
        RoomDraftRecordEntity entity = new RoomDraftRecordEntity();
        entity.setId(reqVO.getRoomDraftRecordId());
        entity.setValid(0);
        roomDraftRecordService.updateById(entity);
    }

    private void inserRoomJoins(Token info, RoomsEntity room) {
        RoomJoinsEntity oldJoin = buildJoin(room, info);

        //代码优化  创建直播间得时候  一定是 新增
        roomJoinsMapper.insert(oldJoin);
    }

    private void inserRoomSupply(RoomsEntity room) {
        RoomSupplyEntity supply = buildSupply(room);
        supplyMapper.insert(supply);
    }

    private void associatedPendant(RoomsEntity room, CreateRoomVO reqVO) {
        if (room.getLiveType().equals(LiveRoomTypeEnum.ON_DEMAND.getType())) {

            LambdaQueryWrapper<PendantMappingEntity> wrapper = Wrappers.lambdaQuery();
            wrapper.eq(PendantMappingEntity::getRoomId, room.getRoomId());
            pendantMappingMapper.delete(wrapper);

            if (reqVO.getPendantId() != null) {
                Token accountInfo = TokenThreadLocal.getToken();
                String creatorId = "0";
                if (accountInfo != null && accountInfo.getAccountId() != null) {
                    creatorId = accountInfo.getAccountId().toString();
                }
                PendantMappingEntity pendantMappingEntity = new PendantMappingEntity();
                pendantMappingEntity.setRoomId(room.getRoomId());
                pendantMappingEntity.setPendantId(reqVO.getPendantId());
                pendantMappingEntity.setCreatorId(creatorId);
                pendantMappingMapper.insert(pendantMappingEntity);

                // 更新挂件参数
                roomInnerService.updateConfigValueByRoomId(room.getRoomId(), StatusEnum.OPEN.getValue(), RoomConfigStatusEnum.PENDANT_STATUS.getValue());
            } else {
                // 更新挂件参数
                roomInnerService.updateConfigValueByRoomId(room.getRoomId(), StatusEnum.CLOSE.getValue(), RoomConfigStatusEnum.PENDANT_STATUS.getValue());
            }

            // 删除挂件缓存
            String key = PendantConstant.PENDANT_FIXED_INFO_KEY + reqVO.getRoomId();
            stringRedisTemplate.delete(key);

        }
    }


    /**
     * 批量创建房间功能开关表并插入
     *
     * @param room 房间数据
     */
    private void batchInsertRoomConfig(RoomsEntity room) {
        List<RoomConfig> configList = new ArrayList<>();
        LocalDateTime now = LocalDateTime.now();

        RoomConfigStatusEnum[] values = RoomConfigStatusEnum.values();
        for (RoomConfigStatusEnum roomConfigStatusEnum : values) {

            RoomConfig config = new RoomConfig();
            config.setRoomId(room.getRoomId());
            config.setAccountId(room.getAccountId());
            config.setIlId(room.getIlId());
            config.setConfigKey(roomConfigStatusEnum.getValue());
            config.setConfigValue(roomConfigStatusEnum.getDefauleValue());
            config.setCreatedAt(now);
            config.setUpdatedAt(now);
            configList.add(config);

        }

        roomConfigMapper.batchInsert(configList);

    }

    /**
     * 构造 roomJoin对象
     *
     * @param room 房间信息
     * @param info token令牌信息
     * @return 房间加入情况信息
     */
    private RoomJoinsEntity buildJoin(RoomsEntity room, Token info) {
        RoomJoinsEntity join = new RoomJoinsEntity();
        join.setRoomId(room.getRoomId());
        join.setAccountId(room.getAccountId());
        join.setNickname(info.getNickname());
        join.setUsername(info.getUsername());
        join.setRoleName(com.vhall.component.plugin.common.constant.RoomConstant.HOST);
        join.setDeviceType(com.vhall.component.plugin.common.constant.RoomConstant.DEFAULT_ZERO);
        join.setDeviceStatus(com.vhall.component.plugin.common.constant.RoomConstant.DEFAULT_ZERO);
        join.setAvatar("");
        return join;
    }

    /**
     * 构造 roomSupply
     *
     * @param room 直播房间对象
     * @return 直播房间补充信息对象
     */
    private RoomSupplyEntity buildSupply(RoomsEntity room) {
        RoomSupplyEntity supply = new RoomSupplyEntity();
        supply.setRoomId(room.getRoomId());
        supply.setAccountId(room.getAccountId());
        supply.setMode(1);
        supply.setIlId(room.getIlId());
        supply.setCreatedAt(room.getCreatedAt());
        supply.setUpdatedAt(room.getUpdatedAt());
        supply.setAssistantSign(RandomUtil.getRandomNum(100000, 900000).toString());
        supply.setAnchorSign(RandomUtil.getRandomNum(100000, 900000).toString());
        if (!(room.getLiveType() != null && com.vhall.component.plugin.common.constant.RoomConstant.PURE == room.getLiveType())) {
            supply.setInteractionSign(RandomUtil.getRandomNum(100000, 900000).toString());
        }
        return supply;
    }

    /**
     * 构造 room对象
     *
     * @param reqVO           room create param
     * @param liveRoom        live room info
     * @param hdRoom          hd room info
     * @param channelRoom     channel room
     * @param nifyChannelRoom notify channel
     * @return room created info
     */
    private RoomsEntity buildRoom(CreateRoomVO reqVO, String liveRoom, String hdRoom, String channelRoom, String nifyChannelRoom, Token info) {
        RoomsEntity room = new RoomsEntity();
        room.setAccountId(Integer.parseInt(String.valueOf(info.getAccountId())));
        room.setRoomId(liveRoom);
        room.setChannelId(channelRoom);
        room.setNifyChannel(nifyChannelRoom);
        room.setInavId(hdRoom);
        room.setSubject(reqVO.getName());

        LocalDateTime begin = LocalDateTime.now();
        if (StringUtils.isNotBlank(reqVO.getBeginTime())) {
            begin = LocalDateTimeUtil.parse(reqVO.getBeginTime(), DatePattern.NORM_DATETIME_PATTERN);
        }
        room.setStartTime(begin);
        long milliSecond = room.getStartTime().toEpochSecond(ZoneOffset.of("+8"));
        room.setBeginTimeStamp(Integer.parseInt(String.valueOf(milliSecond)));
        room.setAppId(appId);
        room.setIntroduction(StringUtils.isBlank(reqVO.getIntroduction()) ? "" : reqVO.getIntroduction());
        room.setCreatedAt(LocalDateTime.now());
        room.setUpdatedAt(LocalDateTime.now());
        // 设置审核状态
        setAuditStatus(room);
        room.setTeacherName("");
        if (StringUtils.isNotBlank(reqVO.getTopics())) {
            room.setTopics(reqVO.getTopics());
        }
        room.setLiveType(reqVO.getLiveType());
        room.setShareIntroduction(reqVO.getShareIntroduction());
        room.setVideoType(reqVO.getVideoType());
        room.setPosterUrl(reqVO.getPosterUrl());
        room.setLimitType(LimitTypeEnum.WATCH_ALL.getValue());
        switch (Objects.requireNonNull(LiveRoomTypeEnum.parse(reqVO.getLiveType()))) {
            case LIVE_BROADCAST:
                break;
            case FAKE_LIVE_BROADCAST:
                room.setDemandVodId(reqVO.getDemandVodId());
                break;
            case ON_DEMAND:
                room.setRecordId(reqVO.getDemandVodId());
                break;
            default:
                break;
        }
        room = buildGuoXinRoomInfo(room, reqVO);
        return room;
    }

    /**
     * 构建国信项目新增的房间信息
     *
     * @param room
     * @param reqVO
     * @return
     */
    private RoomsEntity buildGuoXinRoomInfo(RoomsEntity room, CreateRoomVO reqVO) {
        room.setColumnId(reqVO.getColumnId());
        room.setLiveContentType(reqVO.getLiveContentType());
        String accountVerification = StrUtil.isBlank(reqVO.getAccountVerification()) ? "0" : reqVO.getAccountVerification();
        room.setAccountVerification(accountVerification);
        room.setRiskLive(reqVO.getRiskLive());
        room.setInvestLimit(reqVO.getInvestLimit());
        room.setInvestType(reqVO.getInvestType());
        room.setPermissions(reqVO.getPermissions());
        room.setQualifiedInvestorRequirements(reqVO.getQualifiedInvestorRequirements());
        room.setLiveBusiness(reqVO.getLiveBusiness());
        if (Objects.isNull(room.getIlId()) || room.getIlId() == 0) {
            Token token = TokenThreadLocal.getToken();
            String orgCode = token.getOrgCode();
            String orgName = token.getOrgName();
            room.setOrgCode(orgCode);
            room.setOrgName(orgName);
        }
        room.setLiveTarget(reqVO.getLiveTarget());
        room.setReviewOaId(reqVO.getReviewOaId());
        room.setReviewOaName(reqVO.getReviewOaName());
        room.setReviewOaUrl(reqVO.getReviewOaUrl());
        room.setLiveRormal(reqVO.getLiveRormal());
        room.setWelcomMessage(reqVO.getWelcomMessage());
        Integer warmType = reqVO.getWarmType();
        room.setWarmType(warmType);
        if (Objects.equals(RoomsWarmTypeEnum.VIDEO.getValue(),warmType)) {
            if (StrUtil.isNotBlank(reqVO.getWarmVodId())) {
                room.setWarmVodId(reqVO.getWarmVodId());
            }else{
                throw new BusinessException(BizErrorCode.BIZ_ROOM_WARM_VOID_EMPTY);
            }
        }
        room.setLiveInlet(reqVO.getLiveInlet());

        if (Objects.equals(LivePaidStatusEnum.PAID_LIVE.getValue(),reqVO.getIsPaid())) {
            room.setIsPaid(LivePaidStatusEnum.PAID_LIVE.getValue());
            String paidProductCode = reqVO.getPaidProductCode();
            room.setPaidProductCode(paidProductCode);
            room.setPaidPrice("");
            room.setOriginalPrice("");
        }else{
            room.setIsPaid(LivePaidStatusEnum.FREE_LIVE.getValue());
            room.setPaidProductCode("");
        }

        Integer limitUserScope = reqVO.getLimitUserScope();
        if (Objects.nonNull(limitUserScope)) {
            room.setLimitUserScope(limitUserScope);
        }else{
            room.setLimitUserScope(LiveLimitUserScopeEnum.ALL_USER_VISIBLE.getValue());
        }
        return room;
    }

    /**
     * 设置审核状态
     */
    private void setAuditStatus(RoomsEntity room) {

        //管理员编辑房间，不改变房间审核状态
        Token token = TokenThreadLocal.getToken();
        if (token != null && token.getAccountId() != null) {
            //        开启房间审核时房间默认为送审
            Integer auditStatus = RoomAuditWhichEnum.OPEN_ROOM_AUDIT.getStatus().equals(roomAuditWitch)
                    ? RoomAuditStatusEnum.SUBMIT_FOR_APPROVAL.getAuditStatus()
                    : RoomAuditStatusEnum.APPROVED.getAuditStatus();
//            只有正式直播才做状态重置
            if (Objects.equals(room.getLiveRormal(), 1)) {
                room.setAuditStatus(auditStatus);
                room.setAuditNote("");
            }
        }


    }


    /**
     * 构造公共参数
     *
     * @return
     */
    private HashMap<String, Object> buildCommonParam() {
        HashMap<String, Object> params = new HashMap<>(16);
        params.put("signed_at", DateUtil.currentSeconds());
        params.put("app_id", appId);
        //构建sign
        params.put("sign", paasService.sign(params));
        return params;
    }

    @Override
    public RoomConsoleListRespVO list(Integer accountId, RoomConsoleListVO reqVO) {
        RoomConsoleListRespVO resp = new RoomConsoleListRespVO();
        int currPage = reqVO.getPage() == null ? 1 : reqVO.getPage();
        int pageSize = reqVO.getPageSize() == null ? 20 : reqVO.getPageSize();
        Page<RoomsEntity> page = new Page<>(currPage, pageSize);

        // 构建查询对象
        LambdaQueryWrapper<RoomsEntity> wrapper = assemblyWrapper(reqVO);

        page = roomMapper.selectPage(page, wrapper);

        // 把查询到的 RoomsEntity 分页对象转成 list 集合包装的 RoomsEntityVO对象
        List<RoomsEntityVO> rooms = roomsEntityToListVo(page);

        resp.setRoomList(rooms);
        resp.setCurrentPage(currPage);
        resp.setLastPage((int) page.getPages());
        resp.setPerPage(pageSize);
        resp.setTotal((int) page.getTotal());
        resp.setFrom((currPage - 1) * pageSize + 1);
        resp.setTo(Math.min(currPage * pageSize, (int) page.getTotal()));
        int lastPage = (int) page.getPages();
        resp.setFirstPageUrl("\\/?page=1");
        resp.setLastPageUrl(RoomConstant.PAGE_ESCAPE + (int) page.getPages());
        String prevPage = currPage == 1 ? null : RoomConstant.PAGE_ESCAPE + (currPage - 1);
        String nextPage = currPage == lastPage ? null : RoomConstant.PAGE_ESCAPE + (currPage + 1);
        resp.setPrevPageUrl(prevPage);
        resp.setNextPageUrl(nextPage);
        resp.setPath("\\/");
        return resp;
    }

    @Override
    public RoomConsoleListRespVO complianceCheckList(Integer accountId, RoomConsoleListVO reqVO) {
        RoomConsoleListRespVO resp = new RoomConsoleListRespVO();
        int currPage = reqVO.getPage() == null ? 1 : reqVO.getPage();
        int pageSize = reqVO.getPageSize() == null ? 20 : reqVO.getPageSize();
        Page<RoomsEntity> page = new Page<>(currPage, pageSize);

        // 构建查询对象
        LambdaQueryWrapper<RoomsEntity> wrapper = assemblyWrapper(reqVO);
        if (reqVO.getComplianceCheckStatus() == null) {
            wrapper.ne(RoomsEntity::getComplianceCheckStatus, RoomAuditStatusEnum.SUBMIT_FOR_APPROVAL.getAuditStatus());
        }
        page = roomMapper.selectPage(page, wrapper);

        // 把查询到的 RoomsEntity 分页对象转成 list 集合包装的 RoomsEntityVO对象
        List<RoomsEntityVO> rooms = roomsEntityToListVo(page);

        resp.setRoomList(rooms);
        resp.setCurrentPage(currPage);
        resp.setLastPage((int) page.getPages());
        resp.setPerPage(pageSize);
        resp.setTotal((int) page.getTotal());
        resp.setFrom((currPage - 1) * pageSize + 1);
        resp.setTo(Math.min(currPage * pageSize, (int) page.getTotal()));
        int lastPage = (int) page.getPages();
        resp.setFirstPageUrl("\\/?page=1");
        resp.setLastPageUrl(RoomConstant.PAGE_ESCAPE + (int) page.getPages());
        String prevPage = currPage == 1 ? null : RoomConstant.PAGE_ESCAPE + (currPage - 1);
        String nextPage = currPage == lastPage ? null : RoomConstant.PAGE_ESCAPE + (currPage + 1);
        resp.setPrevPageUrl(prevPage);
        resp.setNextPageUrl(nextPage);
        resp.setPath("\\/");
        return resp;
    }

    @Override
    public List<InvestRefResponseVO> investRefList(Integer ilId) {
        // 投资依据信息
        List<RoomsEntity> records = new ArrayList<>();
        RoomsEntity roomsEntity = new RoomsEntity();
        roomsEntity.setIlId(ilId);
        records.add(roomsEntity);
        Map<Integer, List<InvestRefResponseVO>> investMap = getInvestInfo(records);
        List<InvestRefResponseVO> vos = new ArrayList<>();
        if (!investMap.isEmpty()) {
            List<InvestRefResponseVO> investRefEntities = investMap.get(ilId);
            if (CollectionUtils.isNotEmpty(investRefEntities)) {
                buildInvestRefResponseVO(vos, investRefEntities);
            }
        }
        return vos;
    }

    private void buildInvestRefResponseVO(List<InvestRefResponseVO> vos, List<InvestRefResponseVO> investRefEntities) {
        for (InvestRefResponseVO investRefEntity : investRefEntities) {
            InvestRefResponseVO vo = new InvestRefResponseVO();
            BeanUtil.copyProperties(investRefEntity, vo);
            vo.setRefTypeName(RefTypeEnum.getNameByValue(vo.getRefType()));
            vo.setRefRankName(RefRankEnum.getNameByValue(vo.getRefRank()));
            vo.setRefFormName(RefFormEnum.getNameByValue(vo.getRefForm()));
            vo.setRefStatusName(RefStatuEnum.getNameByValue(vo.getRefStatus()));
            vos.add(vo);
        }
    }

    @Override
    public RoomConsoleListRespVO roomAuditList(RoomAuditConsoleListVO reqVO) {
        RoomConsoleListRespVO resp = new RoomConsoleListRespVO();
        int currPage = reqVO.getPage() == null ? 1 : reqVO.getPage();
        int pageSize = reqVO.getPageSize() == null ? 20 : reqVO.getPageSize();
        Page<RoomsEntity> page = new Page<>(currPage, pageSize);

        LambdaQueryWrapper<RoomsEntity> queryWrapper = Wrappers.lambdaQuery();
        String keyword = reqVO.getKeyword();
        String endTime = reqVO.getEndTime();
        Integer auditStatus = reqVO.getAuditStatus();
        String beginTime = reqVO.getBeginTime();
        queryWrapper.ne(RoomsEntity::getAuditStatus, RoomAuditStatusEnum.SUBMIT_FOR_APPROVAL.getAuditStatus());
        queryWrapper.eq(Objects.nonNull(auditStatus), RoomsEntity::getAuditStatus, auditStatus);
        queryWrapper.eq(RoomsEntity::getLiveRormal, 1);
        queryWrapper.ge(StringUtils.isNotBlank(beginTime), RoomsEntity::getStartTime, beginTime + " " + ConstStr.TIME_ZERO);
        queryWrapper.le(StringUtils.isNotBlank(endTime), RoomsEntity::getStartTime, endTime + " " + ConstStr.TIME_FULL);
        String userPermission = getUserPermission();
        queryWrapper.eq(StringUtils.isNotBlank(userPermission), RoomsEntity::getOrgCode, userPermission);
        queryWrapper.and(StrUtil.isNotBlank(keyword)
                , w -> w.eq(RoomsEntity::getIlId, keyword)
                        .or().like(RoomsEntity::getSubject, keyword)
                        .or().apply("EXISTS(select anchor_room_relation.id from anchor_room_relation where anchor_room_relation.il_id=rooms.il_id and anchor_room_relation.user_name like CONCAT('%',{0},'%')  )", keyword));
        queryWrapper.orderByDesc(RoomsEntity::getStartTime);
        page = roomMapper.selectPage(page, queryWrapper);

        // 把查询到的 RoomsEntity 分页对象转成 list 集合包装的 RoomsEntityVO对象
        List<RoomsEntityVO> rooms = roomsEntityToListVo(page);

        resp.setRoomList(rooms);
        resp.setCurrentPage(currPage);
        resp.setLastPage((int) page.getPages());
        resp.setPerPage(pageSize);
        resp.setTotal((int) page.getTotal());
        resp.setFrom((currPage - 1) * pageSize + 1);
        resp.setTo(Math.min(currPage * pageSize, (int) page.getTotal()));
        int lastPage = (int) page.getPages();
        resp.setFirstPageUrl("\\/?page=1");
        resp.setLastPageUrl(RoomConstant.PAGE_ESCAPE + (int) page.getPages());
        String prevPage = currPage == 1 ? null : RoomConstant.PAGE_ESCAPE + (currPage - 1);
        String nextPage = currPage == lastPage ? null : RoomConstant.PAGE_ESCAPE + (currPage + 1);
        resp.setPrevPageUrl(prevPage);
        resp.setNextPageUrl(nextPage);
        resp.setPath("\\/");
        return resp;
    }

    @Override
    public Boolean inspectionRoom(RoomInspectionReqVO vo) {

        Preconditions.checkArgument(vo.getInspectionStatus().equals(RoomAuditStatusEnum.APPROVED.getAuditStatus()), "不支持此状态！");

        RoomsEntityDTO roomsEntityDTO = new RoomsEntityDTO();
        roomsEntityDTO.setInspectionStatus(RoomAuditStatusEnum.APPROVED.getAuditStatus());
        roomsEntityDTO.setIlId(vo.getIlId());
        roomInnerService.updateByIl(roomsEntityDTO);

        return true;
    }

    @Override
    public List<RoomQrCodeListRspVO> qrCodeList(String roomId) {
        LambdaQueryWrapper<RoomQrCodeEntity> where = Wrappers.lambdaQuery();
        where.eq(RoomQrCodeEntity::getRoomId, roomId);
        where.eq(StringUtils.isNotBlank(getUserPermission()), RoomQrCodeEntity::getOrgCode, getUserPermission());
        where.eq(RoomQrCodeEntity::getDeleted, DeleteStatusEnum.NO_DELETE.getStatus());
        where.orderByDesc(RoomQrCodeEntity::getCreatedAt);
        List<RoomQrCodeEntity> roomQrCodeEntities = roomQrCodeMapper.selectList(where);

        List<RoomQrCodeListRspVO> vos = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(roomQrCodeEntities)) {
            vos = BeanCopyUtil.batchCopy(roomQrCodeEntities, RoomQrCodeListRspVO::new);
        }
        return vos;
    }

    @Override
    public Boolean genQrCode(RoomQrCodeListReqVO vo) {
        // 获取房间信息
        RoomsEntity room = getRoomsEntity(vo.getRoomId());

        RoomQrCodeEntity roomQrCodeEntity = new RoomQrCodeEntity();
        BeanCopyUtil.copy(vo, roomQrCodeEntity);
        addUserInfo(roomQrCodeEntity);
        roomQrCodeEntity.setRoomSubject(room.getSubject());

        // https://weixin.guosen.com.cn/act/actcenter/live_room/dist/pages/live.html?id=10221&outChannel=zuiwxa&qr=webchat

        // https://t-guoxin-java.vhallyun.com/mobile/watch/1787
        String url = qrDefaultUrl + "/" + room.getIlId();
        if (StringUtils.isNotBlank(vo.getQrCode())) {
            url = url + "?qr=" + vo.getQrCode();
        }


        String qrCodeByUrl = qrCodeUtils.generateQRCodeByUrl(url);
        roomQrCodeEntity.setLiveQrCode(qrCodeByUrl);
        roomQrCodeMapper.insert(roomQrCodeEntity);
        return null;
    }

    @Override
    public Boolean delQrCode(Integer id) {
        RoomQrCodeEntity roomQrCodeEntity = roomQrCodeMapper.selectById(id);
        checkUserPermission(roomQrCodeEntity.getOrgCode());

        roomQrCodeEntity.setDeleted(DeleteStatusEnum.DELETE.getStatus());
        roomQrCodeMapper.updateById(roomQrCodeEntity);

        return true;
    }

    @Override
    public Boolean complianceCheck(Integer ilId, Integer auditStatus, String desc) {
        RoomsEntity roomsEntity = roomMapper.selectById(ilId);
        if (roomsEntity == null) {
            throw new BusinessException(BizErrorCode.EMPTY_ROOM);
        }
        if (roomsEntity.getComplianceCheckStatus() == 0) {
            roomsEntity.setComplianceCheckStatus(auditStatus);
            if (StrUtil.isNotEmpty(desc)) {
                roomsEntity.setComplianceCheckNote(desc);
            }
            return roomMapper.updateById(roomsEntity) > 0;
        }
        return false;
    }

    @Override
    public List<LivePriceResponseVO> getLivePriceList() {
        List<LivePriceResponseVO> result = new ArrayList<>();
        LivePriceResponseVO data = new LivePriceResponseVO();
//        todo 等待客户给出真正的对接数据
        data.setProdCode("10001");
        data.setPrice("9.9");
        data.setOriginalPrice("19.90");
        result.add(data);
        data = new LivePriceResponseVO();
        data.setProdCode("10002");
        data.setPrice("29.9");
        data.setOriginalPrice("59.90");
        result.add(data);
        data = new LivePriceResponseVO();
        data.setProdCode("10003");
        data.setPrice("59.9");
        data.setOriginalPrice("99.90");
        result.add(data);
        data = new LivePriceResponseVO();
        data.setProdCode("10004");
        data.setPrice("99.9");
        data.setOriginalPrice("199.90");
        result.add(data);
        data = new LivePriceResponseVO();
        data.setProdCode("10005");
        data.setPrice("399");
        data.setOriginalPrice("599");
        result.add(data);
        return result;
    }

    @Override
    public RoomConsoleListRespVO playbackCheck(RoomConsoleListVO reqVO) {
        RoomConsoleListRespVO resp = new RoomConsoleListRespVO();
        int currPage = reqVO.getPage() == null ? 1 : reqVO.getPage();
        int pageSize = reqVO.getPageSize() == null ? 20 : reqVO.getPageSize();
        Page<RoomsEntity> page = new Page<>(currPage, pageSize);

        // 构建查询对象
        LambdaQueryWrapper<RoomsEntity> wrapper = assemblyWrapper(reqVO);
        wrapper.eq(StrUtil.isNotBlank(getUserPermission()), RoomsEntity::getOrgCode, getUserPermission());
        wrapper.eq(RoomsEntity::getLiveContentType, LiveContentTypeEnum.TYPE_3.getValue());
        wrapper.eq(RoomsEntity::getPlaybackStatus, RoomAuditStatusEnum.APPROVED.getAuditStatus());
        if (reqVO.getPlaybackStatusSecond() != null) {
            wrapper.eq(RoomsEntity::getPlaybackStatusSecond, reqVO.getPlaybackStatusSecond());
        }

        page = roomMapper.selectPage(page, wrapper);

        // 把查询到的 RoomsEntity 分页对象转成 list 集合包装的 RoomsEntityVO对象
        List<RoomsEntityVO> rooms = roomsEntityToListVo(page);

        resp.setRoomList(rooms);
        resp.setCurrentPage(currPage);
        resp.setLastPage((int) page.getPages());
        resp.setPerPage(pageSize);
        resp.setTotal((int) page.getTotal());
        resp.setFrom((currPage - 1) * pageSize + 1);
        resp.setTo(Math.min(currPage * pageSize, (int) page.getTotal()));
        int lastPage = (int) page.getPages();
        resp.setFirstPageUrl("\\/?page=1");
        resp.setLastPageUrl(RoomConstant.PAGE_ESCAPE + (int) page.getPages());
        String prevPage = currPage == 1 ? null : RoomConstant.PAGE_ESCAPE + (currPage - 1);
        String nextPage = currPage == lastPage ? null : RoomConstant.PAGE_ESCAPE + (currPage + 1);
        resp.setPrevPageUrl(prevPage);
        resp.setNextPageUrl(nextPage);
        resp.setPath("\\/");
        return resp;
    }
    @Transactional(rollbackFor = Exception.class)
    @Override
    public LimitScopeWhiteListUploadResponseVO limitScopeWhiteListUpload(LimitScopeWhiteListUploadRequestVO vo) {
        LimitScopeWhiteListUploadResponseVO result = new LimitScopeWhiteListUploadResponseVO();

        MultipartFile multipartFile = vo.getFile();
        String fileName = multipartFile.getOriginalFilename();
        if (StrUtil.isNotBlank(vo.getFileName())) {
            fileName = vo.getFileName();
        }
        String extName = FileUtil.extName(fileName);
        if (StringUtils.isBlank(fileName)) {
            throw new BusinessException(BizErrorCode.COMP_FILTER_UPLOAD_NULL);
        }
        if (!EXT_NAME_LIST.contains(extName)) {
            throw new BusinessException(BizErrorCode.TYPE_FILE_INVALID);
        }
        List<String> phoneStrList ;
        try (InputStream inputStream = multipartFile.getInputStream()) {
            ExcelReader reader;
            reader = ExcelUtil.getReader(inputStream,0);
            int rowCount = reader.getRowCount();
            if (rowCount > 1001) {
                throw new BusinessException(BizErrorCode.COMP_WHITE_LIMIT_IMPORT_OVERFLOW);
            }
            if(rowCount <= 1){
                throw new BusinessException(BizErrorCode.COMP_WHITE_LIMIT_IMPORT_EMPTY);
            }
            List<Object> phoneObjList = reader.readColumn(0, 1);
            phoneStrList = phoneObjList.stream().map(Object::toString).filter(StrUtil::isNotBlank).collect(Collectors.toList());
            for (String phone : phoneStrList) {
                if (!ReUtil.isMatch("^1[3456789]\\d{9}$",phone)) {
                    throw new BusinessException(BizErrorCode.TYPE_PHONE);
                }
            }
            RoomWhiteFileRelationEntity entity = new RoomWhiteFileRelationEntity();
            entity.setFileName(fileName);
            Token token = TokenThreadLocal.getToken();
            Integer accountId = token.getAccountId();
            entity.setUpdateBy(accountId);
            entity.setCreateBy(accountId);
            whiteFileRelationService.save(entity);
            Integer fileId = entity.getId();
            List<RoomWhitePhoneRelationEntity> whitePhoneRelationEntities = phoneStrList.stream().map(data -> {
                RoomWhitePhoneRelationEntity temp = new RoomWhitePhoneRelationEntity();
                temp.setRoomWhiteFileId(fileId);
                temp.setPhone(data);
                return temp;
            }).collect(Collectors.toList());
            whitePhoneRelationService.saveBatch(whitePhoneRelationEntities);
            result.setFileName(fileName);
            result.setId(fileId);
        }catch (IOException e){
            log.error("解析客户手机号白名单文件异常",e);
            throw new BusinessException(BizErrorCode.EMPTY_FILE_ERROR);
        }
        return result;
    }



    @Override
    public Boolean consoleEndLive(ConsoleEndLiveRequestVO vo) {
        String roomId = vo.getRoomId();
        RoomsEntityDTO roomsEntityDTO = Optional.ofNullable(roomInnerService.getRoomInfo(roomId)).orElseThrow(() -> new BusinessException(BizErrorCode.EMPTY_ROOM));
        Token token = TokenThreadLocal.getToken();
        return roomService.endLiveMain(roomId, roomsEntityDTO, token.getAccountId());
    }

    /**
     * 构建查询对象
     */
    private LambdaQueryWrapper<RoomsEntity> assemblyWrapper(RoomConsoleListVO reqVO) {
        LambdaQueryWrapper<RoomsEntity> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(StringUtils.isNotBlank(getUserPermission()), RoomsEntity::getOrgCode, getUserPermission());


        wrapper.ge(StringUtils.isNotBlank(reqVO.getBeginTime()), RoomsEntity::getStartTime, reqVO.getBeginTime() + " " + ConstStr.TIME_ZERO);
        wrapper.le(StringUtils.isNotBlank(reqVO.getEndTime()), RoomsEntity::getStartTime, reqVO.getEndTime() + " " + ConstStr.TIME_FULL);

        wrapper.ge(StringUtils.isNotBlank(reqVO.getCreatedBeginTime()), RoomsEntity::getCreatedAt, reqVO.getCreatedBeginTime() + " " + ConstStr.TIME_ZERO);
        wrapper.le(StringUtils.isNotBlank(reqVO.getCreatedEndTime()), RoomsEntity::getCreatedAt, reqVO.getCreatedEndTime() + " " + ConstStr.TIME_FULL);

        if (Objects.equals(RoomConsoleListSortColumnEnum.CREATED_AT.getValue(), reqVO.getSortColumn())) {
            wrapper.orderByDesc(RoomsEntity::getCreatedAt);
        } else {
            wrapper.orderByDesc(RoomsEntity::getStartTime);
        }

        String keyword = reqVO.getKeyword();
        wrapper.and(StringUtils.isNotBlank(keyword), r -> r.like(RoomsEntity::getSubject, keyword)
                .or()
                .like(RoomsEntity::getIlId, keyword)
                .or()
                .apply("EXISTS(SELECT anchor_room_relation.id FROM anchor_room_relation WHERE anchor_room_relation.il_id = rooms.il_id AND anchor_room_relation.user_name like CONCAT('%',{0},'%') )", keyword));

        wrapper.eq(reqVO.getAuditStatus() != null, RoomsEntity::getAuditStatus, reqVO.getAuditStatus());
        wrapper.eq(reqVO.getOptionStatus() != null, RoomsEntity::getStatus, reqVO.getOptionStatus());
        if (reqVO.getPlaybackStatus() != null) {
            wrapper.eq(RoomsEntity::getPlaybackStatus, reqVO.getPlaybackStatus());
            wrapper.and(e -> e.ne(RoomsEntity::getRecordId, null).or().ne(RoomsEntity::getRecordId, ""));
        }
        wrapper.eq(reqVO.getComplianceCheckStatus() != null, RoomsEntity::getComplianceCheckStatus, reqVO.getComplianceCheckStatus());
        wrapper.eq(reqVO.getLiveRormal() != null, RoomsEntity::getLiveRormal, reqVO.getLiveRormal());
        if (CollectionUtils.isNotEmpty(reqVO.getLiveTypes())) {
            wrapper.in(RoomsEntity::getLiveType, reqVO.getLiveTypes());
        }
        wrapper.eq(StrUtil.isNotBlank(reqVO.getOrgCode()),RoomsEntity::getOrgCode,reqVO.getOrgCode());
        return wrapper;
    }


    @Override
    public AppRoomListRespVO listForApp(Integer accountId, AppRoomListReqVO reqVO) {
        AppRoomListRespVO resp = new AppRoomListRespVO();
        int currPage = reqVO.getPage() == null ? 1 : reqVO.getPage();
        int pageSize = reqVO.getPageSize() == null ? 20 : reqVO.getPageSize();
        Page<RoomsEntity> page = new Page<>(currPage, pageSize);
        LambdaQueryWrapper<RoomsEntity> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(StringUtils.isNotBlank(getUserPermission()), RoomsEntity::getOrgCode, getUserPermission());
        wrapper.orderByDesc(RoomsEntity::getIlId);
        page = roomMapper.selectPage(page, wrapper);


        // 把查询到的 RoomsEntity 分页对象转成 list 集合包装的 RoomsEntityVO对象
        List<RoomsEntityVO> rooms = roomsEntityToListVo(page);

        resp.setRoomList(rooms);
        resp.setCurrentPage(currPage);
        resp.setLastPage((int) page.getPages());
        resp.setPerPage(pageSize);
        resp.setTotal((int) page.getTotal());
        resp.setFrom((currPage - 1) * pageSize + 1);
        resp.setTo(Math.min(currPage * pageSize, (int) page.getTotal()));
        return resp;
    }


    /**
     * 获取权限 嘉宾 助理 观众
     *
     * @param reqVO
     * @return
     */
    @Override
    public RoomSupplyRespVO getCompetence(RoomLimitsVO reqVO) {
        RoomsEntityDTO roomsEntityDTO = roomInnerService.getRoomInfo(reqVO.getRoomId());
        if (roomsEntityDTO == null) {
            throw new BusinessException(BizErrorCode.EMPTY_ROOM);
        }

        RoomSupplyEntity supplyEntity = supplyMapper.selectById(roomsEntityDTO.getIlId());
        return createRoomSupply(roomsEntityDTO, supplyEntity);
    }

    /**
     * 构造  supply权限
     *
     * @param room
     * @param supplyEntity
     * @return
     */
    private RoomSupplyRespVO createRoomSupply(RoomsEntityDTO room, RoomSupplyEntity supplyEntity) {
        RoomSupplyRespVO vo = new RoomSupplyRespVO();
        vo.setIlId(room.getIlId());
        vo.setName(room.getSubject());
        vo.setSubject(room.getSubject());
        vo.setStartTime(room.getStartTime());
        vo.setCreatedAt(room.getCreatedAt());
        vo.setBeginTime(room.getCreatedAt());
        if (supplyEntity != null) {
            vo.setAssistantSign(supplyEntity.getAssistantSign());
            vo.setInteractionSign(supplyEntity.getInteractionSign());
            vo.setAnchorSign(supplyEntity.getAnchorSign());
        } else {
            vo.setAssistantSign("");
            vo.setInteractionSign("");
            vo.setAnchorSign("");
        }
        vo.setWatchUrl(webDomain + "/live-room/watch/" + room.getIlId());
        vo.setViewUrl(webDomain + "/live-room/watch/" + room.getIlId());
        vo.setAnchorUrl(webDomain + "/select-live-type/" + room.getIlId());
        vo.setLiveType(room.getLiveType());
        vo.setLiveMode(room.getLiveMode());
        if (room.getAccountId() != null) {
            AccountEntity accountEntity = accountMapper.selectById(room.getAccountId());
            if (accountEntity != null && StringUtils.isNotBlank(accountEntity.getNickname())) {
                vo.setNickname(accountEntity.getNickname());
            }
        }
        return vo;
    }


    @Override
    public JSONObject getRoomStatus(@Validated RoomLimitsVO reqVO) {
        RoomsEntity room = getRoomsEntity(reqVO.getRoomId());
        JSONObject obj = new JSONObject();
        obj.put("status", room.getStatus());
        return obj;
    }

    @SuppressWarnings("unchecked")
    @Override
    public InavRoomGetRespVO getRoom(String roomId, Integer accountId) {

        RoomsEntity room = getRoomsEntity(roomId);
        InavRoomGetRespVO resp = new InavRoomGetRespVO();
        setRoomDetails(room, resp);
        buildWarmInfo(room);


        // 拼装视频基础信息
        assemblyVideoData(resp, room);

        // 获取审核人信息
        resp.setReviewerName(assemblyReviewer(room));
//        暖场信息
        resp.setWarmInfo(roomInnerService.getRoomWarmInfo(room.getIlId()));
//        主播信息
        resp.setAnchorInfoList(getAnchorRoomInfo(room.getIlId()));
//        投资依据信息
        resp.setInvestRefList(getRoomInvestRefList(room.getIlId()));
//        适当性豁免产品的编号产品编号列表
        resp.setExemptProdCodes(getRoomExemptProdCodes(room.getIlId()));
//        仅限持仓产品的客户可见产品编号列表
        resp.setVisibleProdCodes(getRoomVisibleProdCodes(room.getIlId()));
//        白名单文件信息
        WhiteFileDTO roomWhiteFileInfo = getRoomWhiteFileInfo(room.getIlId());
        resp.setWhiteFileInfo(roomWhiteFileInfo);
        if (Objects.nonNull(roomWhiteFileInfo)) {
            resp.setWhiteFileId(roomWhiteFileInfo.getId());
        }
        // 挂件信息
        assenmblyPendant(resp);
        resp.setGoods(getGoods(room.getIlId()));
        // 房间config信息
        // 获取当前房间配置信息
        Map<String, List<RoomConfig>> roomConfigMap = getRoomConfigListMap(Collections.singletonList(room));
        assemblyRoomConfig(resp, roomConfigMap);
        return resp;
    }

    /**
     * 获取房间关联的白名单文件信息
     * @param ilId
     * @return
     */
    private WhiteFileDTO getRoomWhiteFileInfo(Integer ilId) {
        LambdaQueryWrapper<RoomWhiteFileRelationEntity> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(RoomWhiteFileRelationEntity::getIlId,ilId).last("limit 1");
        RoomWhiteFileRelationEntity one = whiteFileRelationService.getOne(queryWrapper);
        WhiteFileDTO result = new WhiteFileDTO();
        if (Objects.nonNull(one)) {
            result.setId(one.getId());
            result.setFileName(one.getFileName());
            result.setFileUrl(one.getFileUrl());
        }
        return result;
    }

    /**
     * 获取适当性豁免关联的产品编码
     * @param ilId
     * @return
     */
    private List<String> getRoomExemptProdCodes(Integer ilId) {
        return getRoomProdRelationOfCodes(ilId,RoomAssociationTypeEnum.EXEMPTION_ASSOCIATION.getValue());
    }
    /**
     * 获取观看范围限定购买产品的产品编码
     * @param ilId
     * @return
     */
    private List<String> getRoomVisibleProdCodes(Integer ilId) {
        return getRoomProdRelationOfCodes(ilId,RoomAssociationTypeEnum.PURCHASE_ASSOCIATION.getValue());
    }
    /**
     *
     * @param ilId
     * @param purchaseAssociationValue 关联房间类型| 0：适当性豁免关联产品 1：观看范围限定购买产品
     * @return
     */
    @NotNull
    private List<String> getRoomProdRelationOfCodes(Integer ilId, Integer purchaseAssociationValue) {
        LambdaQueryWrapper<RoomProdRelationEntity> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(RoomProdRelationEntity::getIlId, ilId)
                .eq(RoomProdRelationEntity::getType, purchaseAssociationValue)
                .orderByAsc(RoomProdRelationEntity::getId);
        List<RoomProdRelationEntity> list = roomProdRelationService.list(queryWrapper);
        List<String> ofCodes = list.stream().map(RoomProdRelationEntity::getOfcode).filter(StrUtil::isNotBlank).distinct().collect(Collectors.toList());
        return ofCodes;
    }



    /**
     * 构造  WarnInfo
     *
     * @param room
     */
    private void buildWarmInfo(RoomsEntity room) {
        JSONObject obj = new JSONObject();
        obj.put("type", room.getWarmType());
        obj.put("vod_name", "");
        obj.put("vod_id", "");
        obj.put("img", "");
        String warmVodId = room.getWarmVodId();
        if (room.getWarmType() == 0) {
            obj.put("img", room.getCoverImage());
        } else if (!StringUtils.isBlank(warmVodId)) {
            RecordEntity recordEntity = recordOfRoomMapper.findByVodId(warmVodId);
            if (recordEntity != null) {
                obj.put("vod_id", warmVodId);
                obj.put("vod_name", recordEntity.getName());
            }
        }
        redisTemplate.opsForValue().set(RedisKey.WARM_INFO + room.getIlId(), obj.toJSONString());
    }



    private List<SearchGoodsResponseVO> getGoods(Integer ilId) {
        PageVO<SearchGoodsResponseVO> pageVO = guoxinPrdService.searchRoomGoods(ilId, 1, 100);
        return pageVO.getData();
    }

    /**
     * 获取和房间关联的投资依据信息列表
     *
     * @param ilId 房间id
     * @return
     */
    private List<InvestRefResponseVO> getRoomInvestRefList(Integer ilId) {
        RoomInvestRefRelationListRequestVO vo = new RoomInvestRefRelationListRequestVO();
        vo.setIlId(ilId);
        return investRefService.getRoomInvestRefRelationList(vo);
    }

    private void assemblyRoomConfig(InavRoomGetRespVO resp, Map<String, List<RoomConfig>> roomConfigMap) {
        if (CollectionUtils.isNotEmpty(roomConfigMap) && CollectionUtils.isNotEmpty(roomConfigMap.get(resp.getRoomId()))) {
            List<RoomConfig> roomConfigs = roomConfigMap.get(resp.getRoomId());
            Map<String, Integer> integerMap = roomConfigs.stream().collect(Collectors.toMap(RoomConfig::getConfigKey, RoomConfig::getConfigValue, (e1, e2) -> e1));

            resp.setReservationStatus(integerMap.get(RoomConfigStatusEnum.RESERVATION_STATUS.getValue()));
            resp.setCommentStatus(integerMap.get(RoomConfigStatusEnum.COMMENT_STATUS.getValue()));
            resp.setChatManagementLiveStatus(integerMap.get(RoomConfigStatusEnum.CHAT_MANAGEMENT_LIVE.getValue()));
            resp.setChatManagementPlaybackStatus(integerMap.get(RoomConfigStatusEnum.CHAT_MANAGEMENT_PLAYBACK.getValue()));
            resp.setGiftLiveStatus(integerMap.get(RoomConfigStatusEnum.CHAT_MANAGEMENT_GIFT.getValue()));
        }
    }

    @Override
    public InavRoomGetRespVO getRoomByIlId(RoomAdminGetInfoVO reqVO) {
        RoomsEntity roomsEntityByIlId = getRoomsEntityByIlId(reqVO);
        if (roomsEntityByIlId == null) {
            throw new BusinessException(BizErrorCode.EMPTY_ROOM);
        }
        return getRoom(roomsEntityByIlId.getRoomId(), null);
    }


    private void assenmblyPendant(InavRoomGetRespVO resp) {
        List<PendantRspVO> pendantRspVOS = pendantConsoleService.bindingList(PendantListReqVO.builder().roomId(resp.getRoomId()).build());
        if (CollectionUtils.isNotEmpty(pendantRspVOS)) {
            for (PendantRspVO pendantRspVO : pendantRspVOS) {
                baseCommonService.convertUrl(pendantRspVO, "pic");
            }
        }
        resp.setPendants(pendantRspVOS);
    }


    private void assemblyVideoData(InavRoomGetRespVO resp, RoomsEntity room) {

        String vodId = "";
        switch (Objects.requireNonNull(LiveRoomTypeEnum.parse(room.getLiveType()))) {
            case LIVE_BROADCAST:
                break;
            case FAKE_LIVE_BROADCAST:
                vodId = room.getDemandVodId();
                break;
            case ON_DEMAND:
                vodId = room.getRecordId();
                break;
            default:
                break;
        }

        if (StringUtils.isNotBlank(vodId)) {
            LambdaQueryWrapper<RecordEntity> where = Wrappers.lambdaQuery();
            where.eq(RecordEntity::getVodId, vodId);
            List<RecordEntity> recordEntities = recordMapper.selectList(where);
            if (CollectionUtils.isNotEmpty(recordEntities)) {
                resp.setSelectVideoData(SelectVideoData.builder().demandVodId(vodId).name(recordEntities.get(0).getName()).build());
            }
        }
    }

    private void setRoomDetails(RoomsEntity room, InavRoomGetRespVO roomRespVO) {

        BeanCopyUtil.copy(room, roomRespVO);

        roomRespVO.setIlId(room.getIlId());
        roomRespVO.setRoomId(room.getRoomId());
        roomRespVO.setSubject(room.getSubject());
        roomRespVO.setAccountId(Long.parseLong(room.getAccountId().toString()));
        roomRespVO.setInavId(room.getInavId());
        roomRespVO.setChannelId(room.getChannelId());
        roomRespVO.setNifyChannel(room.getNifyChannel());
        roomRespVO.setRecordId(room.getRecordId());
        roomRespVO.setStartTime(room.getStartTime());
        roomRespVO.setIntroduction(room.getIntroduction());
        roomRespVO.setCategory(room.getCategory());
        roomRespVO.setCoverImage(room.getCoverImage());
        // 对topic做特殊处理
        List<TagVO> tags = new ArrayList<>();
        if (StringUtils.isNotBlank(room.getTopics())) {
            List<String> tagList = Splitter.on(",").splitToList(room.getTopics());
            LambdaQueryWrapper<TagEntity> wrapper = Wrappers.lambdaQuery();
            wrapper.in(TagEntity::getTagId, tagList);
            List<TagEntity> tagEntities = tagOfRoomMapper.selectList(wrapper);
            if (CollectionUtils.isNotEmpty(tagEntities)) {
                TagVO vo;
                for (TagEntity single : tagEntities) {
                    vo = new TagVO();
                    vo.setName(single.getName());
                    vo.setTagId(single.getTagId());
                    tags.add(vo);
                }
            }
        }
        roomRespVO.setTopics(tags);
        roomRespVO.setLayout(room.getLayout());
        roomRespVO.setStatus(room.getStatus());
        roomRespVO.setIsDelete(room.getIsDelete());
        roomRespVO.setMessageApproval(room.getMessageApproval());
        roomRespVO.setCreatedAt(room.getCreatedAt());
        roomRespVO.setUpdatedAt(room.getUpdatedAt());
        roomRespVO.setAppId(room.getAppId());
        roomRespVO.setLiveType(room.getLiveType());
        roomRespVO.setLike(room.getLike());
        roomRespVO.setWarmType(room.getWarmType());
        roomRespVO.setWarmVodId(room.getWarmVodId());
        roomRespVO.setMode(room.getMode());
        roomRespVO.setIsOpenDocument(room.getIsOpenDocument());
        roomRespVO.setLimitType(room.getLimitType());
        roomRespVO.setTeacherName(room.getTeacherName());
        roomRespVO.setBeginLiveTime(room.getBeginLiveTime());
        roomRespVO.setEndLiveTime(room.getEndLiveTime());
        roomRespVO.setBeginTime(room.getStartTime());
        roomRespVO.setBeginTimeStamp(room.getBeginTimeStamp().longValue());
        roomRespVO.setDesc(room.getIntroduction());
        roomRespVO.setName(room.getSubject());
        roomRespVO.setImage(room.getCoverImage());
        roomRespVO.setAuditNote(room.getAuditNote());
        roomRespVO.setAuditStatus(room.getAuditStatus());
    }

    @Override
    public Boolean saveWarm(WarmVO reqVO, Integer accountId) {

        if (reqVO.getType() == 1 && StringUtils.isBlank(reqVO.getVodId())) {
            throw new BusinessException(BizErrorCode.BIZ_INVALID_PARAM);
        }
        // 如果输入了vodId，就校验一下vodid是否是真是的
        if (StringUtils.isNotBlank(reqVO.getVodId())) {
            RecordEntity recordEntity = recordOfRoomMapper.findByVodId(reqVO.getVodId());
            if (null == recordEntity) {
                throw new BusinessException(BizErrorCode.EMPTY_VIDEO);
            }
        }
        RoomsEntity room = getRoomsEntity(reqVO.getRoomId());

        // 校验一下用户信息里的accountid和room里的accountid是否相等。
        if (reqVO.getType() != null) {
            room.setWarmType(reqVO.getType());
            if (!StringUtils.isBlank(reqVO.getVodId())) {
                room.setWarmVodId(reqVO.getVodId());
            }
            room.setUpdatedAt(LocalDateTime.now());

            roomInnerService.updateByIl(RoomsEntityDTO.builder()
                    .ilId(room.getIlId())
                    .roomId(room.getRoomId())
                    .warmType(reqVO.getType())
                    .warmVodId(reqVO.getVodId())
                    .updatedAt(LocalDateTime.now()).build());

            buildWarmInfo(room, reqVO);
        }

        return true;
    }

    @Override
    public WarmRespVO getWarmInfo(WarmReqVO reqVO) {
        WarmRespVO result = new WarmRespVO();

        RoomsEntityDTO room = roomInnerService.getRoomInfo(reqVO.getRoomId());
        if (room == null) {
            throw new BusinessException(BizErrorCode.EMPTY_ROOM);
        }
        RecordEntity recordEntity = recordOfRoomMapper.findByVodId(room.getWarmVodId());
        result.setType(room.getWarmType());
        result.setVodId(room.getWarmVodId());
        result.setVodName(recordEntity == null ? "" : recordEntity.getName());
        return result;
    }

    /**
     * 构造  WarnInfo
     *
     * @param room
     */
    private void buildWarmInfo(RoomsEntity room, WarmVO reqVO) {
        JSONObject obj = new JSONObject();
        obj.put("type", room.getWarmType());
        obj.put("vod_name", "");
        obj.put("vod_id", "");
        obj.put("img", "");
        if (reqVO.getType() == 0) {
            obj.put("img", room.getCoverImage());
        } else if (!StringUtils.isBlank(reqVO.getVodId())) {
            RecordEntity recordEntity = recordOfRoomMapper.findByVodId(reqVO.getVodId());
            if (recordEntity != null) {
                obj.put("vod_id", reqVO.getVodId());
                obj.put("vod_name", recordEntity.getName());
            }
        }
        redisTemplate.opsForValue().set(RedisKey.WARM_INFO + room.getIlId(), obj.toJSONString());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean update(CreateRoomNewVO reqVO) {
        return editRoom(reqVO, reqVO.getImageUrl(), true);
    }

    @Override
    public Boolean update(CreateRoomVO reqVO, String imageBase64, Boolean imageIsUpdate) {
        String url = coverImageUpload(imageBase64);
        return editRoom(reqVO, url, imageIsUpdate);
    }


    private boolean editRoom(CreateRoomVO vo, String coverUrl, Boolean imageIsUpdate) {

        Preconditions.checkArgument(StringUtils.isNotBlank(vo.getRoomId()), "房间号不能为空！");

        // 直播中禁止编辑
        RoomsEntity room = Optional.ofNullable(prohibitEditing(vo.getRoomId())).orElseThrow(() -> new BusinessException(BizErrorCode.EMPTY_ROOM));

        if (room != null && room.getHotStatus().equals(StatusEnum.OPEN.getValue())) {
            // 已经设置为热点的视频，禁止删除竖版海报
            if (StringUtils.isBlank(vo.getPosterUrl())) {
                throw new BusinessException("热点视频，不能删除竖版海报!");
            }
        }

        basicValidateVo(vo);

        Integer columnId = vo.getColumnId();
        if (Objects.isNull(columnId)) {
            room.setColumnId(0);
        }
        // 如果image上传的是空的话。设为空串。
        if (imageIsUpdate) {
            room.setCoverImage(coverUrl);
        }

        switch (Objects.requireNonNull(LiveRoomTypeEnum.parse(room.getLiveType()))) {
            case ON_DEMAND:
                // 如果当前视频不是原来视频，释放原来视频
                if (!vo.getDemandVodId().equals(room.getRecordId())) {
                    releaseRecord(room);
                }
                room.setRecordId(vo.getDemandVodId());
                break;
            case FAKE_LIVE_BROADCAST:
                // 如果当前视频不是原来视频，释放原来视频
                if (!vo.getDemandVodId().equals(room.getDemandVodId())) {
                    releaseRecord(room);
                }
                room.setDemandVodId(vo.getDemandVodId());
            default:
                break;
        }

        room.setSubject(vo.getName());

        LocalDateTime begin = LocalDateTime.now();
        if (StringUtils.isNotBlank(vo.getBeginTime())) {
            begin = LocalDateTimeUtil.parse(vo.getBeginTime(), DatePattern.NORM_DATETIME_PATTERN);
        }
        room.setStartTime(begin);
        long milliSecond = room.getStartTime().toEpochSecond(ZoneOffset.of("+8"));
        room.setBeginTimeStamp(Integer.parseInt(String.valueOf(milliSecond)));
        room.setIntroduction(StringUtils.isBlank(vo.getIntroduction()) ? "" : vo.getIntroduction());
        room.setShareIntroduction(StringUtils.isBlank(vo.getShareIntroduction()) ? "" : vo.getShareIntroduction());
        room.setUpdatedAt(LocalDateTime.now());
        room.setTopics(vo.getTopics());
        room.setVideoType(vo.getVideoType());
        room.setPosterUrl(vo.getPosterUrl());
        buildGuoXinRoomInfo(room, vo);

        // 设置审核状态
        setAuditStatus(room);

        int i = roomMapper.updateById(room);
        boolean result = i > 0;
        if (result) {
            handleUpdateRoomAfter(vo, room);
        }
        return result;
    }

    private void handleUpdateRoomAfter(CreateRoomVO vo, RoomsEntity room) {
        deleteRoomInfoToCache(room);

        // 录播视频加入缓存，给定时调度使用 ;开启了审核，就是待审核状态；关闭审核，就是审核通过状态
        int auditStatus = getRoomAuditStatus(room);
        addRecordedVideoToCache(room, auditStatus);
//录制直播-关联到音视频管理
        recordVideoRelevanceRoom(room);

        // 点播类型的直播，如果有挂件，要进行关联
        associatedPendant(room, vo);
        // 主播账号信息
        relationRoomToAnchor(vo, room);
        // 投资依据关联信息
        relationRoomToInvestRef(vo, room);
//        关联国信的私募产品信息
        relationRoomToGuoxinProd(vo, room);
//        关联客户可见数据逻辑的处理
        relationRoomToLimitUserScopeInfo(vo,room);
//        构建房间暖场视频信息
        buildWarmInfo(room);
    }

    /**
     * 获取定时直播所需审核状态的逻辑
     * @param room
     * @return
     */
    private int getRoomAuditStatus(RoomsEntity room) {
//        开启了审核，就是待审核状态；关闭审核，就是审核通过状态
        int auditStatus = RoomAuditWhichEnum.OPEN_ROOM_AUDIT.getStatus().equals(roomAuditWitch) ? RoomAuditStatusEnum.CHECK_PENDING.getAuditStatus() : RoomAuditStatusEnum.APPROVED.getAuditStatus();
//        测试直播直接就返回通过审核
        if (Objects.equals(room.getLiveRormal(), LiveRormalEnum.TEST_LIVE.getValue())) {
            auditStatus = RoomAuditStatusEnum.APPROVED.getAuditStatus();
        }
        return auditStatus;
    }
    /**
     * 关联房间和投资依据
     *
     * @param vo
     * @param room
     */
    private void relationRoomToInvestRef(CreateRoomVO vo, RoomsEntity room) {
        List<InvestRefVO> investRefList = vo.getInvestRefList();
        Integer ilId = room.getIlId();
        if (CollUtil.isNotEmpty(investRefList)) {
            LambdaQueryWrapper<InvestRefEntity> investRefWhere = Wrappers.lambdaQuery();
            List<Integer> investRefIds = investRefList.stream().map(InvestRefVO::getInvestRefId).collect(Collectors.toList());
            investRefWhere.in(InvestRefEntity::getId, investRefIds);
            List<InvestRefEntity> investRefEntities = investRefService.list(investRefWhere);
            if (CollUtil.isNotEmpty(investRefEntities)) {
                AddInvestRefRelationVO dto = new AddInvestRefRelationVO();
                dto.setIlId(ilId);
                dto.setInvestRefIds(investRefIds);
                investRefRelationService.addInvestRefRelation(dto);
            }
        } else {
            LambdaQueryWrapper<InvestRefRelationEntity> deleteWhere = Wrappers.lambdaQuery();
            deleteWhere.eq(InvestRefRelationEntity::getIlId, ilId);
            investRefRelationService.remove(deleteWhere);
        }
    }

    /**
     * 关联主播到房间
     *
     * @param vo
     * @param room
     */
    private void relationRoomToAnchor(CreateRoomVO vo, RoomsEntity room) {
        List<ThirdUserVO> anchorInfoList = vo.getAnchorInfoList();
        if (CollUtil.isNotEmpty(anchorInfoList)) {
            Token info = TokenThreadLocal.getToken();
            anchorRoomRelationService.relationRoomToThirdUser(info.getAccountId(), room.getIlId(), anchorInfoList);
        }
    }

    private void releaseRecord(RoomsEntity room) {
        LambdaUpdateWrapper<RecordEntity> wrapper = Wrappers.lambdaUpdate();
        wrapper.eq(RecordEntity::getVodId, room.getRecordId())
                .set(RecordEntity::getIlId, "0")
                .set(RecordEntity::getRoomId, "");
        recordOfRoomMapper.update(null, wrapper);
    }


    private void deleteRoomInfoToCache(RoomsEntity info) {
        // 设置两个缓存，一个是roonId,一个是ilId。在房间数据更新的时候删除掉缓存。
        String roomId = info.getRoomId();
        Integer ilId = info.getIlId();
        redisTemplate.delete(RedisKey.ROOM_INFO_BY_ROOM_ID + roomId);
        redisTemplate.delete(RedisKey.ROOM_INFO_BY_ROOM_ID + ilId);
        redisTemplate.delete(RedisKey.ROOM_ANCHOR_LIST_CACHE + ilId);
    }

    @Override
    public JSONObject getStreamAddress(RoomLimitsVO reqVO) {

        LocalDateTime time;
        if ("test".equals(active) || "dev".equals(active)) {
            time = LocalDateTime.now().plusMinutes(3);
        } else {
            time = LocalDateTime.now().plusDays(30);
        }
        String expireTime = LocalDateTimeUtil.format(time, DatePattern.NORM_DATETIME_FORMATTER);
        JSONObject obj = paasService.getPushInfo(reqVO.getRoomId(), expireTime);
        if (obj.getInteger(HttpConstant.CODE) == HttpConstant.SUCCESS_CODE) {
            return obj.getJSONObject(HttpConstant.DATA);
        }
        return null;
    }

    @Override
    public JSONObject getAccessToken(RoomLimitsVO reqVO) {
        Token accountInfo = TokenThreadLocal.getToken();
        RoomsEntity room = getRoomsEntity(reqVO.getRoomId());
        Map<String, Object> param = buildAccessTokenMap(room, accountInfo.getAccountId());
        JSONObject result = paasService.createAccessToken(param);
        String token = "";
        if (result.getInteger(HttpConstant.CODE) == HttpConstant.SUCCESS_CODE) {
            token = result.getJSONObject(HttpConstant.DATA).getString("access_token");
        }
        JSONObject resp = new JSONObject();
        resp.put("access_token", token);
        return resp;
    }

    /**
     * 构造 accessToken得  map
     *
     * @param room
     * @return
     */
    private Map<String, Object> buildAccessTokenMap(RoomsEntity room, Integer accountId) {
        Map<String, Object> param = new HashMap<>(16);
        param.put("kick_inav", room.getInavId());
        param.put("publish_inav_stream", room.getInavId());
        param.put("askfor_publish_inav", room.getInavId());
        param.put("publish_inav_another", room.getInavId());
        param.put("audit_publish_inav", room.getInavId());
        param.put("apply_inav_publish", room.getInavId());
        param.put("publish_stream", room.getRoomId());
        param.put("kick_inav_stream", room.getInavId());
        param.put("operate_document", room.getChannelId());
        param.put("chat", room.getChannelId());
        param.put("third_party_user_id", accountId);
        return param;
    }

    @Override
    public Map<String, Object> getOnlineCheck(OnlineCheckVO reqVO) {

        Map<String, Object> map = new HashMap<>(16);

        List<Integer> accountIdList = new ArrayList<>();
        String[] accs = reqVO.getAccountIds().split(",");
        String accountId = null;
        for (String acc : accs) {
            if (!StringUtils.isBlank(acc)) {
                accountIdList.add(Integer.parseInt(acc));
                accountId = acc;
                map.put(acc, 0);
            }
        }

        map = paasService.checkUserOnlineByChannel(reqVO.getChannelId(), accountIdList);
        Integer conn = (Integer) map.get(accountId);
        //如果 在线  加一个 流状态判断  如果  在线 并且流状态也是开的  那么不允许开播
        if (conn == 1) {
            map.put(accountId, 0);
            LambdaQueryWrapper<RoomsEntity> where = Wrappers.lambdaQuery();
            where.eq(RoomsEntity::getChannelId, reqVO.getChannelId());
            RoomsEntity rooms = roomMapper.selectOne(where);

            JSONObject result = paasService.getStreamStatus(rooms.getRoomId());

            log.info("获取得 流状态是: " + result);
            if (result.getInteger("code") != 200) {
                log.error("拉取paas流状态列表错误");
            }
            JSONObject data = result.getJSONObject("data");
            JSONObject obj = data.getJSONObject(rooms.getRoomId());

            if (obj.getInteger("stream_status") == com.vhall.component.plugin.common.constant.RoomConstant.STATUS_START) {
                map.put(accountId, 1);
            }
        }

        return map;

    }


    @Override
    public JSONObject listCustomTag(CustomMenuVO reqVO) {
        LambdaQueryWrapper<RoomSupplyEntity> where = Wrappers.lambdaQuery();
        where.eq(RoomSupplyEntity::getRoomId, reqVO.getRoomId());
        RoomSupplyEntity supply = supplyMapper.selectOne(where);
        if (supply == null) {
            throw new BusinessException(BizErrorCode.EMPTY_ROOM);
        }
        JSONObject obj = new JSONObject();
        obj.put("il_id", supply.getIlId());
        obj.put("custom_tag", supply.getCustomTag());
        return obj;
    }

    @Override
    public JSONObject editCustomTag(CustomMenuEditVO reqVO) {

        RoomsEntityDTO room = roomInnerService.getRoomInfo(reqVO.getRoomId());
        if (room == null) {
            throw new BusinessException(BizErrorCode.EMPTY_ROOM);
        }
        JSONObject obj = new JSONObject();
        obj.put("il_id", room.getIlId());
        RoomSupplyEntity supply = supplyMapper.selectById(room.getIlId());
        if (StringUtils.isBlank(reqVO.getCustomTag())) {
            return obj;
        }
        if (supply != null) {
            supply.setCustomTag(reqVO.getCustomTag());
            supply.setUpdatedAt(LocalDateTime.now());
            supplyMapper.updateById(supply);
            obj.put("custom_tag", supply.getCustomTag());
        }
        //获取自定义菜单的菜单数量
        int size = JsonUtil.arrayFromJSONString(reqVO.getCustomTag(), CustomTagDTO.class).size();
        //判断是否数量是否>2 (默认2个菜单,大于说明添加了自定义菜单)

        roomInnerService.updateConfigValueByRoomId(reqVO.getRoomId(), size > 2 ? 1 : 0, RoomConfigStatusEnum.CUSTOM_TAG_STATUS.getValue());


        return obj;
    }

    @Override
    public Boolean addLiveMode(AddLiveModeVO reqVO) {
        RoomsEntity room = getRoomsEntity(reqVO.getRoomId());

        int i = roomInnerService.updateByIl(RoomsEntityDTO.builder()
                .ilId(room.getIlId()).roomId(room.getRoomId())
                .liveMode(reqVO.getLiveMode())
                .updatedAt(LocalDateTime.now()).build());

        return i > 0;
    }

    /**
     * 单条新增流地址
     *
     * @param reqVO
     * @return
     */
    @Override
    public Boolean addPushStream(PushStreamReqVO reqVO) {
        //校验推流地址是否合法
        extracted(reqVO);
        //校验roomid
        roomInnerService.checkRoomId(reqVO.getRoomId());
        //校验推流地址数量
        Integer count = pushStreamMapper.selectCount(Wrappers.<PushStreamEntity>lambdaQuery().eq(PushStreamEntity::getRoomId, reqVO.getRoomId()));
        if (count >= MAX_PUSH) {
            throw new BusinessException(BizErrorCode.BIZ_PUSH_STREAM_LIMIT);
        }
        //校验重复
        List<PushStreamEntity> pushStreamEntityList = pushStreamMapper.selectList(Wrappers.<PushStreamEntity>lambdaQuery()
                .eq(PushStreamEntity::getRoomId, reqVO.getRoomId())
                .eq(PushStreamEntity::getRtmps, reqVO.getRtmps())
                .eq(PushStreamEntity::getSkey, reqVO.getKey()));
        if (!pushStreamEntityList.isEmpty()) {
            throw new BusinessException(BizErrorCode.BIZ_REPEAT_CREATE);
        }
        PushStreamEntity pushStreamEntity = getPushStreamEntity(reqVO);
        return pushStreamMapper.insert(pushStreamEntity) > 0;


    }


    /**
     * 批量新增流地址
     *
     * @param reqVO
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean addPushStreamList(String reqVO) {
        if (StringUtils.isBlank(reqVO)) {
            throw new BusinessException(BizErrorCode.TYPE_INVALID_STRING);
        }
        List<PushStreamReqVO> addPushStreamReqVOList;
        String decode;
        try {
            decode = URLDecoder.decode(reqVO, CommonConstant.UTF8);
            addPushStreamReqVOList = JSON.parseArray(decode, PushStreamReqVO.class);
        } catch (UnsupportedEncodingException e) {
            throw new BusinessException(BizErrorCode.TYPE_INVALID_STRING);
        }
        if (addPushStreamReqVOList.isEmpty()) {
            throw new BusinessException(BizErrorCode.TYPE_INVALID_STRING);
        }
        //校验参数
        validParam(addPushStreamReqVOList);
        for (int i = 0; i < addPushStreamReqVOList.size(); i++) {
            //插入推流记录表
            PushStreamEntity pushStreamEntity = getPushStreamEntity(addPushStreamReqVOList.get(i));
            pushStreamMapper.insert(pushStreamEntity);
        }
        return true;

    }

    /**
     * 单条删除流地址
     *
     * @param id
     * @return
     */
    @Override
    public Boolean delPushStreamsById(Integer id) {
        //校验数据是否存在
        PushStreamEntity pushStreamEntity = Optional.ofNullable(pushStreamMapper.selectById(id)).<BusinessException>orElseThrow(() -> {
            throw new BusinessException(BizErrorCode.EMPTY_DATA);
        });
        //0推流未开始 1推流中 2推流结束 3推流失败 推流中的数据不可删除
        switch (pushStreamEntity.getStatus()) {
            case 0:
            case 2:
            case 3:
                return pushStreamMapper.deleteById(id) > 0;
            case 1:
                throw new BusinessException(BizErrorCode.EMPTY_PUSH_STREAM_START);
            default:
                return false;

        }

    }


    /**
     * 单条编辑流地址
     *
     * @param reqVO
     * @return
     */
    @Override
    public Boolean editPushStream(PushStreamReqVO reqVO) {
        //校验id
        if (Objects.isNull(reqVO.getId()) || reqVO.getId() == 0) {
            throw new BusinessException(BizErrorCode.EMPTY_PUSH_STREAM_ID);
        }
        //校验推流地址是否合法
        extracted(reqVO);
        //校验room_id
        roomInnerService.checkRoomId(reqVO.getRoomId());
        //判断当前编辑的记录是否存在
        PushStreamEntity pushStreamEntity = pushStreamMapper.selectById(reqVO.getId());
        if (Objects.isNull(pushStreamEntity)) {
            throw new BusinessException(BizErrorCode.EMPTY_DATA);
        }
        //判断当前编辑的数据是否重复
        PushStreamEntity ps = pushStreamMapper.selectOne(Wrappers.<PushStreamEntity>lambdaQuery()
                .eq(PushStreamEntity::getRoomId, reqVO.getRoomId())
                .eq(PushStreamEntity::getRtmps, reqVO.getRtmps())
                .eq(PushStreamEntity::getSkey, reqVO.getKey()));

        if (!Objects.isNull(ps) && !ps.getId().equals(reqVO.getId())) {
            throw new BusinessException(BizErrorCode.BIZ_EDIT_REPEAT_FAILED);
        }

        //校验定时推流时间
        if (Objects.nonNull(reqVO.getCronPushTime()) && reqVO.getCronPushTime().isBefore(LocalDateTime.now())) {
            throw new BusinessException(BizErrorCode.BIZ_PUSH_STREAM_CRONTIME_ERROR);
        }

        LambdaUpdateWrapper<PushStreamEntity> update = Wrappers.lambdaUpdate();
        update.eq(PushStreamEntity::getId, reqVO.getId());

        update.set(PushStreamEntity::getType, reqVO.getType());
        update.set(PushStreamEntity::getCronPushTime, reqVO.getCronPushTime());
        update.set(PushStreamEntity::getName, reqVO.getName());
        update.set(PushStreamEntity::getRtmps, reqVO.getRtmps());
        update.set(PushStreamEntity::getSkey, reqVO.getKey());
        update.set(PushStreamEntity::getUpdatedAt, LocalDateTime.now());
        update.set(PushStreamEntity::getStartTime, Objects.nonNull(reqVO.getCronPushTime()) ? reqVO.getCronPushTime() : null);
        update.set(PushStreamEntity::getEndTime, Objects.nonNull(reqVO.getCronPushTime()) ? reqVO.getCronPushTime().plusDays(1) : null);
        update.set(PushStreamEntity::getStatus, 0);
        return pushStreamMapper.update(null, update) > 0;

    }

    /**
     * 获取流地址详情
     *
     * @param id
     * @return
     */
    @Override
    public PushStreamSingleRspVO getPushStreamDeatil(Integer id) {
        //校验id是否存在
        PushStreamEntity pushStreamEntity = Optional.ofNullable(pushStreamMapper.selectById(id)).<BusinessException>orElseThrow(() -> new BusinessException(BizErrorCode.EMPTY_DATA));
        PushStreamSingleRspVO pushStreamSingleRspVO = new PushStreamSingleRspVO();
        BeanUtils.copyProperties(pushStreamEntity, pushStreamSingleRspVO);
        return pushStreamSingleRspVO;
    }

    /**
     * 获取推流列表
     *
     * @return
     */
    @Override
    public List<PushStreamSingleRspVO> getPushStreamList(PushStreamListReqVO reqVO) {
        //校验roomid
        roomInnerService.checkRoomId(reqVO.getRoomId());
        Page<PushStreamEntity> page = new Page<>(reqVO.getPage(), reqVO.getPageSize());
        LambdaQueryWrapper<PushStreamEntity> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(PushStreamEntity::getRoomId, reqVO.getRoomId());
        Page<PushStreamEntity> pageResult = pushStreamMapper.selectPage(page, wrapper);
        List<PushStreamSingleRspVO> resData = new ArrayList<>();
        if (Objects.nonNull(pageResult) && Objects.nonNull(pageResult.getRecords()) && !pageResult.getRecords().isEmpty()) {
            pageResult.getRecords().forEach(r -> {
                PushStreamSingleRspVO pushStreamSingleRspVo = new PushStreamSingleRspVO();
                BeanUtils.copyProperties(r, pushStreamSingleRspVo);
                resData.add(pushStreamSingleRspVo);
            });
        }
        return resData;
    }

    /**
     * 单条推流
     *
     * @param id 推流id
     * @return 是否推流成功
     */
    @Override
    public Boolean pushStreamStart(Integer id) {
        //校验id是否存在
        PushStreamEntity pushStreamEntity = Optional.ofNullable(pushStreamMapper.selectById(id)).<BusinessException>orElseThrow(() -> new BusinessException(BizErrorCode.BIZ_STREAMING_NOT_EXIST));
        RoomsEntityDTO roomInfo = roomInnerService.getRoomInfo(pushStreamEntity.getRoomId());
        //房间未开播 推流失败
        if (roomInfo.getStatus() != 1) {
            throw new BusinessException(BizErrorCode.BIZ_NOT_LIVE_PUSH_STREAM_FAIL);
        }
        //推流中 重复推流
        if (pushStreamEntity.getStatus() == 1) {
            throw new BusinessException(BizErrorCode.BIZ_STREAMING_NOT_REPEAT);
        }
        //校验推流地址是否重复，重复发起推流
        List<PushStreamEntity> pushStreamEntityList = pushStreamMapper.selectList(Wrappers.<PushStreamEntity>lambdaQuery()
                .eq(PushStreamEntity::getRoomId, pushStreamEntity.getRoomId())
                .eq(PushStreamEntity::getRtmps, pushStreamEntity.getRtmps())
                .eq(PushStreamEntity::getSkey, pushStreamEntity.getSkey()));
        if (!pushStreamEntityList.isEmpty()) {
            pushStreamEntityList.forEach(ps -> {
                if (ps.getStatus() == 1) {
                    throw new BusinessException(BizErrorCode.BIZ_STREAMING_NOT_REPEAT);
                }
            });
        }
        //构建paas请求参数
        Map<String, Object> param = new HashMap<>(16);
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime endTime = now.plusDays(1);
        param.put("room_id", pushStreamEntity.getRoomId());
        param.put("oversea", pushStreamEntity.getType());
        param.put("dest_url", StringUtils.isBlank(pushStreamEntity.getSkey()) ? pushStreamEntity.getRtmps() : pushStreamEntity.getRtmps() + "/" + pushStreamEntity.getSkey());
        param.put("start_time", DateUtil.format(now, DatePattern.NORM_DATETIME_PATTERN));
        param.put("end_time", DateUtil.format(endTime, DatePattern.NORM_DATETIME_PATTERN));

        pushStreamEntity.setStartTime(now);
        try {
            JSONObject streamConfig = paasService.startpushStreamConfig(param);
            pushStreamEntity.setConfigId(Integer.parseInt(streamConfig.getString("config_id")));
            pushStreamEntity.setStatus(1);
            return pushStreamMapper.updateById(pushStreamEntity) > 0;
        } catch (Exception e) {
            pushStreamEntity.setStatus(3);
            pushStreamMapper.updateById(pushStreamEntity);
            throw new BusinessException(BizErrorCode.BIZ_PUSH_STREAM_FAIL);
        }
    }

    /**
     * 批量推流
     *
     * @param reqVO 推流信息
     * @return 是否完成
     */
    @Override
    public Boolean pushStreamStartBatch(String reqVO) {
        //校验参数转换
        if (StringUtils.isBlank(reqVO)) {
            throw new BusinessException(BizErrorCode.TYPE_INVALID_STRING);
        }
        List<PushStreamReqVO> pushStreamReqList;
        try {
            String decode = URLDecoder.decode(reqVO, CommonConstant.UTF8);
            pushStreamReqList = JSON.parseArray(decode, PushStreamReqVO.class);
            if (pushStreamReqList.isEmpty()) {
                throw new BusinessException(BizErrorCode.TYPE_INVALID_STRING);
            }
        } catch (Exception e) {
            log.error("pushStreamStartBatch urlDecode异常", e);
            throw new BusinessException(BizErrorCode.TYPE_INVALID_STRING);
        }
        //校验参数是否合法
        validParam(pushStreamReqList);
        pushStreamReqList.forEach(ps -> {
            //插入推流记录表
            PushStreamEntity pushStreamEntity = getPushStreamEntity(ps);
            pushStreamMapper.insert(pushStreamEntity);
            //构建paas请求参数
            Map<String, Object> param = Maps.newHashMap();
            LocalDateTime now = LocalDateTime.now();
            LocalDateTime endTime = now.plusDays(1);
            param.put("room_id", pushStreamEntity.getRoomId());
            param.put("oversea", pushStreamEntity.getType());
            param.put("dest_url", StringUtils.isBlank(pushStreamEntity.getSkey()) ? pushStreamEntity.getRtmps() : pushStreamEntity.getRtmps() + "/" + pushStreamEntity.getSkey());
            param.put("start_time", DateUtil.format(now, DatePattern.NORM_DATETIME_PATTERN));
            param.put("end_time", DateUtil.format(endTime, DatePattern.NORM_DATETIME_PATTERN));
            pushStreamEntity.setStartTime(now);
            pushStreamEntity.setEndTime(endTime);
            try {
                //检查推流状态
                RoomsEntityDTO roomInfo = roomInnerService.getRoomInfo(ps.getRoomId());
                //房间开播状态可以推流，否则推流失败
                if (roomInfo.getStatus() == 1) {
                    JSONObject streamConfig = paasService.startpushStreamConfig(param);
                    pushStreamEntity.setConfigId(Integer.parseInt(streamConfig.getString("config_id")));
                    pushStreamEntity.setStatus(1);
                } else {
                    pushStreamEntity.setStatus(3);
                }
            } catch (Exception e) {
                //3代表推流失败
                pushStreamEntity.setStatus(3);
            }
            pushStreamMapper.updateById(pushStreamEntity);
        });
        return true;
    }

    /**
     * 停止推流
     *
     * @param id 要停止推流的id
     * @return 是否成功
     */
    @Override
    public Boolean pushStreamEnd(Integer id) {
        //校验id参数
        PushStreamEntity pushStreamEntity = Optional.ofNullable(pushStreamMapper.selectById(id)).<BusinessException>orElseThrow(() -> new BusinessException(BizErrorCode.EMPTY_DATA));
        try {
            Map<String, Object> param = Maps.newHashMap();
            Integer configId = pushStreamEntity.getConfigId();
            param.put("config_id", configId);
            paasService.deleteStreamConfig(param);
        } catch (Exception e) {
            log.warn("PaaS停止推流异常:{}", e.getMessage());
        }

        pushStreamEntity.setStatus(2);
        pushStreamEntity.setEndTime(LocalDateTime.now());
        return pushStreamMapper.updateById(pushStreamEntity) > 0;
    }

    /**
     * 推流统计
     *
     * @param roomId 房间id
     * @return 推流统计信息
     */
    @Override
    public PushStreamStatisticsRspVO pushStreamStatistics(String roomId) {
        //校验roomid
        roomInnerService.checkRoomId(roomId);
        PushStreamStatisticsRspVO pushStreamStatisticsRspVo = new PushStreamStatisticsRspVO();
        pushStreamStatisticsRspVo.setRoomId(roomId);
        LambdaQueryWrapper<PushStreamEntity> wrapper = Wrappers.lambdaQuery();
        List<PushStreamEntity> pushStreamEntities = pushStreamMapper.selectList(wrapper.eq(PushStreamEntity::getRoomId, roomId));
        if (pushStreamEntities.isEmpty()) {
            return pushStreamStatisticsRspVo;
        }
        //根据状态统计 0 未开始 1推流中 2推流结束
        pushStreamStatisticsRspVo.setStreamCount(pushStreamEntities.size());
        Map<Integer, List<PushStreamEntity>> collect = pushStreamEntities.stream().collect(Collectors.groupingBy(PushStreamEntity::getStatus));
        pushStreamStatisticsRspVo.setNotBeginCount(Objects.isNull(collect.get(0)) ? 0 : collect.get(0).size());
        pushStreamStatisticsRspVo.setHavingCount(Objects.isNull(collect.get(1)) ? 0 : collect.get(1).size());
        pushStreamStatisticsRspVo.setEndCount(Objects.isNull(collect.get(2)) ? 0 : collect.get(2).size());
        return pushStreamStatisticsRspVo;
    }

    @Override
    public Boolean watermarkSetting(RoomWatermarkVO reqVO) {
        RoomsEntityDTO dto = roomInnerService.getRoomInfo(reqVO.getRoomId());
        if (dto.getStatus() == com.vhall.component.plugin.common.constant.RoomConstant.STATUS_START) {
            // 直播中房间 不允许更新水印
            throw new BusinessException(BizErrorCode.LIVEING_NOT_UPDATE_WARTERMARK);
        }

        // 通知paas 更新房间水印模版ID
        setWatermark(reqVO.getRoomId(), reqVO.getWatermarkTtempId());

        RoomsEntityDTO roomsEntityDTO = new RoomsEntityDTO();
        roomsEntityDTO.setWatermarkTempId(reqVO.getWatermarkTtempId());
        roomsEntityDTO.setRoomId(reqVO.getRoomId());
        roomInnerService.updateByIl(roomsEntityDTO);

        return true;
    }

    @Override
    public RoomWatermarkDTO getRoomWatermark(String roomId) {
        RoomWatermarkDTO result = new RoomWatermarkDTO();
        RoomsEntityDTO room = roomInnerService.getRoomInfo(roomId);
        if (room == null) {
            throw new BusinessException(BizErrorCode.EMPTY_ROOM);
        }
        Integer tempId = null;
        if (room.getWatermarkTempId() == null) {
            tempId = Integer.parseInt(roomInnerService.getDefaultWatermarkTemplateId());
        } else {
            tempId = room.getWatermarkTempId();
        }

        if (tempId == null || tempId == 0) {
            return null;
        }
        JSONObject jsonObject = paasService.getWatermarkInfo(tempId);
        result.setWatermarkImage(jsonObject.getString("watermark_img"));
        result.setWatermarkPosition(jsonObject.getInteger("watermark_positiontype"));
        result.setWatermarkTtempId(tempId);
        result.setRoomId(roomId);
        return result;
    }

    @Override
    public Boolean submitForApproval(SubmitForApprovalVO vo) {

        String key = MessageFormat.format("{0}{1}", SUBMIT_FOR_APPROVAL, vo.getIlId());
        if (!RedisLocker.tryLock(key, "1", 10 * 1000L)) {
            throw new BusinessException("系统繁忙，请稍后重试");
        }

        try {
            dealSubmitForApproval(vo);
        } finally {
            RedisLocker.unlock(key, "1");
        }

        return true;
    }

    private void dealSubmitForApproval(SubmitForApprovalVO vo) {

        RoomsEntityDTO roomInfoById = roomInnerService.getRoomInfoById(vo.getIlId());

        Preconditions.checkArgument(roomInfoById.getAuditStatus().equals(RoomAuditStatusEnum.SUBMIT_FOR_APPROVAL.getAuditStatus())
                , "当前房间状态不能提交送审");

        Integer auditStatus = RoomAuditWhichEnum.OPEN_ROOM_AUDIT.getStatus().equals(roomAuditWitch)
                ? RoomAuditStatusEnum.CHECK_PENDING.getAuditStatus()
                : RoomAuditStatusEnum.APPROVED.getAuditStatus();
        RoomsEntityDTO roomsEntityDTO = RoomsEntityDTO.builder().ilId(vo.getIlId()).updatedAt(LocalDateTime.now())
                .auditStatus(auditStatus).build();
        roomsEntityDTO.setComplianceCheckStatus(auditStatus);
        roomsEntityDTO.setAuditNotifyUser(vo.getUserCode());
        roomInnerService.updateByIl(roomsEntityDTO);

        // 调用国信接口通知
        Token token = TokenThreadLocal.getToken();
        SendInnerMessageUrl sendInnerMessageUrl = new SendInnerMessageUrl();
        sendInnerMessageUrl.setNickName(token.getNickname());
        sendInnerMessageUrl.setNotesid(token.getThirdPartyUserId());
        sendInnerMessageUrl.setName("直播审核");
        sendInnerMessageUrl.setTitle(roomInfoById.getSubject());
        sendInnerMessageUrl.setSendto(vo.getUserCode());
        if (StringUtils.isNotBlank(active) && "prod".equals(active)) {
            sendInnerMessageUrl.setUrl("https://live.web.guosen.com.cn/live/live-audit");
        }else{
            sendInnerMessageUrl.setUrl(MessageFormat.format("{0}/{1}",viewingPath,"live-audit"));
        }
        guoXinService.sendInnerMessageUrl(sendInnerMessageUrl);
    }

    @Override
    public Boolean revokeReview(Integer ilId) {

        String key = MessageFormat.format("{0}{1}", REVOKE_REVIEW, ilId);
        if (!RedisLocker.tryLock(key, "1", 10 * 1000L)) {
            throw new BusinessException("系统繁忙，请稍后重试");
        }

        try {
            dealRevokeReview(ilId);
        } finally {
            RedisLocker.unlock(key, "1");
        }

        return true;
    }

    @Override
    public LiveNotNeedPlayBackResponseVO saveLiveNotNeedPlayBack(SaveLiveNotNeedPlayBackRequestVO vo) {
        Integer ilId = vo.getIlId();
        RoomsEntityDTO room = Optional.ofNullable(roomInnerService.getRoomInfoById(ilId)).orElseThrow(()->new BusinessException(BizErrorCode.EMPTY_ROOM));
        RoomConfig config = getLiveNotNeedPlayBackConfig(ilId);
        LiveNotNeedPlayBackResponseVO result = new LiveNotNeedPlayBackResponseVO();
        if (Objects.isNull(config)) {
            LocalDateTime now = LocalDateTime.now();
            config = new RoomConfig();
            config.setRoomId(room.getRoomId());
            config.setAccountId(room.getAccountId());
            config.setIlId(room.getIlId());
            config.setConfigKey(RoomConfigStatusEnum.NO_NEED_SET_PLAYBACK.getValue());
            config.setConfigValue(vo.getNoNeedSetPlayback());
            config.setCreatedAt(now);
            config.setUpdatedAt(now);
            roomConfigMapper.insert(config);
            result.setIlId(ilId);
            result.setId(config.getId());
            result.setNoNeedSetPlayback(vo.getNoNeedSetPlayback());
        }else {
            config.setConfigValue(vo.getNoNeedSetPlayback());
            roomConfigMapper.updateById(config);
            result.setIlId(ilId);
            result.setId(config.getId());
            result.setNoNeedSetPlayback(vo.getNoNeedSetPlayback());
        }
        return result;
    }

    /**
     * 获取房间的标记无需回放状态配置
     * @param ilId
     * @return
     */
    private RoomConfig getLiveNotNeedPlayBackConfig(Integer ilId) {
        LambdaQueryWrapper<RoomConfig> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(RoomConfig::getIlId, ilId)
        .eq(RoomConfig::getConfigKey,RoomConfigStatusEnum.NO_NEED_SET_PLAYBACK.getValue())
        .last("limit 1")
        .orderByAsc(RoomConfig::getId);
        RoomConfig config = roomConfigMapper.selectOne(queryWrapper);
        return config;
    }

    @Override
    public LiveNotNeedPlayBackResponseVO getLiveNotNeedPlayBack(RoomsBaseRequestVO vo) {
        Integer ilId = vo.getIlId();
        RoomsEntityDTO room = Optional.ofNullable(roomInnerService.getRoomInfoById(ilId)).orElseThrow(()->new BusinessException(BizErrorCode.EMPTY_ROOM));
        RoomConfig config = getLiveNotNeedPlayBackConfig(room.getIlId());
        LiveNotNeedPlayBackResponseVO result = new LiveNotNeedPlayBackResponseVO();
        if (Objects.isNull(config)) {
            result.setIlId(ilId);
            result.setId(0);
            result.setNoNeedSetPlayback(RoomConfigStatusEnum.NO_NEED_SET_PLAYBACK.getDefauleValue());
        }else{
            result.setIlId(ilId);
            result.setId(config.getId());
            result.setNoNeedSetPlayback(config.getConfigValue());
        }
        return result;
    }

    private void dealRevokeReview(Integer ilId) {

        RoomsEntityDTO roomInfoById = roomInnerService.getRoomInfoById(ilId);

        Preconditions.checkArgument(roomInfoById.getAuditStatus().equals(RoomAuditStatusEnum.CHECK_PENDING.getAuditStatus())
                , "当前房间状态不能撤销送审");

        int auditStatus = RoomAuditStatusEnum.SUBMIT_FOR_APPROVAL.getAuditStatus();

        RoomsEntityDTO roomsEntityDTO = RoomsEntityDTO.builder().ilId(ilId).updatedAt(LocalDateTime.now())
                .auditStatus(auditStatus).build();
        roomsEntityDTO.setComplianceCheckStatus(auditStatus);
        roomInnerService.updateByIl(roomsEntityDTO);


        // 调用国信接口通知
        Token token = TokenThreadLocal.getToken();
        SendInnerMessageUrl sendInnerMessageUrl = new SendInnerMessageUrl();
        sendInnerMessageUrl.setNickName(token.getNickname());
        sendInnerMessageUrl.setNotesid(token.getThirdPartyUserId());
        sendInnerMessageUrl.setName("取消直播审核");
        sendInnerMessageUrl.setTitle(roomInfoById.getSubject());
        sendInnerMessageUrl.setSendto(roomInfoById.getAuditNotifyUser());
        if (StringUtils.isNotBlank(active) && "prod".equals(active)) {
            sendInnerMessageUrl.setUrl("https://live.web.guosen.com.cn/live/live-audit");
        }else{
            sendInnerMessageUrl.setUrl(MessageFormat.format("{0}/{1}",viewingPath,"live-audit"));
        }
        guoXinService.sendInnerMessageUrl(sendInnerMessageUrl);
    }

    @Override
    public IntroductionDetailRspVO detail(IntroductionDetailReqVO introductionDetailReqVO) {
        String roomId = introductionDetailReqVO.getRoomId();

        // 获取房间信息
        RoomsEntity room = getRoomsEntity(roomId);

        PageChannelRspVO pageChannelRspVO = new PageChannelRspVO();
        // 获取房间对应频道信息
        if (StrUtil.isNotEmpty(room.getChannelId())) {
            PageChannelEntity pageChannelEntity = pageChannelMapper.selectById(room.getPageChannelId());
            if (pageChannelEntity != null) {
                BeanCopyUtil.copy(pageChannelEntity, pageChannelRspVO);
            }
        }

        // 获取当前频道下面两条数据 ,抛去当前房间的,也抛去短视频的
        LambdaQueryWrapper<RoomsEntity> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(RoomsEntity::getPageChannelId, room.getPageChannelId());
        wrapper.notIn(RoomsEntity::getRoomId, room.getRoomId());
        wrapper.eq(RoomsEntity::getAuditStatus, RoomAuditStatusEnum.APPROVED.getAuditStatus());
        wrapper.eq(RoomsEntity::getLoadOrUnload, StatusEnum.OPEN.getValue());
        wrapper.last("and live_type!=3  and il_id not in ( select b.il_id from rooms b where b.status=2 and (b.record_id is null or b.record_id =''))" +
                "ORDER BY top_status_at desc, FIELD(`status`,1,0,2) , start_time desc, il_id desc  limit 10");
        List<RoomsEntity> roomsEntities = roomMapper.selectList(wrapper);

        // 拼装房间信息
        List<RoomsEntityVO> roomList = assemblyRoomList(roomsEntities);

        ArrayList<RoomsEntityVO> vos = sortResult(roomList);


        IntroductionDetailRspVO introductionDetailRspVO = new IntroductionDetailRspVO();
        introductionDetailRspVO.setPageChannelInfo(pageChannelRspVO);
        introductionDetailRspVO.setRoomList(vos);

        return introductionDetailRspVO;
    }

    /**
     * 有置顶全是按照置顶时间倒序排
     * 直播状态按照 直播中>待直播>回放/短视频;  按照预计开播时间倒序排列 ;短视频按照上架时间倒序排；
     * 回放和短视频是按照时间混排
     *
     * @param rooms
     * @return
     */
    private ArrayList<RoomsEntityVO> sortResult(List<RoomsEntityVO> rooms) {
        ArrayList<RoomsEntityVO> result = new ArrayList<>();

        // 置顶的排序
        List<RoomsEntityVO> topList = rooms.stream().filter(e -> e.getTopStatusAt() != null).collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(topList)) {
            topList.sort(Comparator.comparing(RoomsEntityVO::getTopStatusAt).reversed());
            result.addAll(topList);
        }


        // 没有置顶
        rooms = rooms.stream().filter(e -> e.getTopStatusAt() == null).collect(Collectors.toList());

        Map<Integer, List<RoomsEntityVO>> listMap = rooms.stream().collect(Collectors.groupingBy(RoomsEntityVO::getStatus));


        List<RoomsEntityVO> videoRspVOS = listMap.get(RoomStatusEnum.LIVE_BROADCAST.getStatus());
        if (CollectionUtils.isNotEmpty(videoRspVOS)) {
            List<RoomsEntityVO> collect = videoRspVOS.stream().sorted(Comparator.comparing(RoomsEntityVO::getStartTime).reversed()).collect(Collectors.toList());
            result.addAll(collect);
        }

        List<RoomsEntityVO> videoRspVOS2 = listMap.get(RoomStatusEnum.TO_BE_BROADCAST.getStatus());
        if (CollectionUtils.isNotEmpty(videoRspVOS2)) {
            videoRspVOS2 = videoRspVOS2.stream().filter(e -> !e.getLiveType().equals(LiveRoomTypeEnum.ON_DEMAND.getType())).collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(videoRspVOS2)) {
                List<RoomsEntityVO> collect = videoRspVOS2.stream().sorted(Comparator.comparing(RoomsEntityVO::getStartTime).reversed()).collect(Collectors.toList());
                result.addAll(collect);
            }

        }

        List<RoomsEntityVO> rspVOS = rooms.stream().filter(e -> e.getLiveType().equals(LiveRoomTypeEnum.ON_DEMAND.getType())
                || e.getStatus().equals(RoomStatusEnum.END_BROADCAST.getStatus())).collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(rspVOS)) {
            List<RoomsEntityVO> collect1 = rspVOS.stream().sorted(Comparator.comparing(this::makeKey2).reversed()).collect(Collectors.toList());
            result.addAll(collect1);
        }

        return result;

    }

    private LocalDateTime makeKey2(Object e) {
        RoomsEntityVO temp = (RoomsEntityVO) e;
        if (temp.getLiveType().equals(LiveRoomTypeEnum.ON_DEMAND.getType())) {
            // 短视频按照上架时间倒序排
            return temp.getLoadOrUnloadAt();
        } else {
            return temp.getStartTime();
        }

    }


    /**
     * 拼装房间信息
     *
     * @param roomsEntities
     * @return
     */
    private List<RoomsEntityVO> assemblyRoomList(List<RoomsEntity> roomsEntities) {
        List<RoomsEntityVO> roomList = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(roomsEntities)) {
            roomList = BeanCopyUtil.batchCopy(roomsEntities, RoomsEntityVO::new);

            for (RoomsEntityVO roomsEntityVO : roomList) {
                // 获取房间对应标签信息
                assemblyRoomTopicInfo(roomsEntityVO);

                assemblyPvData(roomsEntityVO);
            }

        }
        return roomList;
    }

    private void assemblyPvData(RoomsEntityVO roomsEntityVO) {

        UserParamVO reqVO = new UserParamVO();
        reqVO.setIlId(roomsEntityVO.getIlId());
        UserBasicStatisticsRspVO ubsrv = accountForDataMapper.getBasicStatisticsInfo(reqVO);
        int pvCount = (ubsrv.getPvCount() != null && ubsrv.getPvCount() > 0) ? ubsrv.getPvCount() : 0;

        UserBasicStatisticsRspVO ubsrvVod = accountForDataMapper.getVodBasicStatisticsInfo(reqVO);
        pvCount += (ubsrvVod.getPvCount() != null && ubsrvVod.getPvCount() > 0) ? ubsrvVod.getPvCount() : 0;

        // 获取房间pv数据
        roomsEntityVO.setPv(pvCount);
    }

    /**
     * 获取房间对应标签信息
     */
    private void assemblyRoomTopicInfo(RoomsEntityVO roomsEntityVO) {
        if (StringUtils.isNotBlank(roomsEntityVO.getTopics())) {
            List<TagVO> tagVOS = new ArrayList<>();
            List<String> tagList = Splitter.on(",").splitToList(roomsEntityVO.getTopics());
            LambdaQueryWrapper<TagEntity> queryWrapper = Wrappers.lambdaQuery();
            queryWrapper.in(TagEntity::getTagId, tagList);
            List<TagEntity> tagEntities = tagOfRoomMapper.selectList(queryWrapper);
            if (CollectionUtils.isNotEmpty(tagEntities)) {
                tagVOS = BeanCopyUtil.batchCopy(tagEntities, TagVO::new);
            }
            roomsEntityVO.setTopicsList(tagVOS);
        }
    }

    @Override
    public FirstFrameFromVideoRspVO getTheFirstFrameOfTheVideo(FirstFrameFromVideoVO reqVO) {

        Preconditions.checkArgument(StringUtils.isNotBlank(reqVO.getDemandVodId()), "视频id不能为空！");
        String key = FIRST_FRAME_FROM_VIDEO + reqVO.getDemandVodId();
        Object value = redisTemplate.opsForValue().get(key);
        FirstFrameFromVideoRspVO firstFrameFromVideoRspVO = new FirstFrameFromVideoRspVO();
        if (value == null) {
            // 缓存为空，表示没有，调用微吼云接口
            paasService.submitCoverTasks(reqVO);
        } else {
            firstFrameFromVideoRspVO.setImageUri(value.toString());
        }
        return firstFrameFromVideoRspVO;
    }

    @Override
    public Boolean reservationStatusRoom(RoomOperationStatusVO vo) {
        // 直播中禁止编辑
        prohibitEditing(vo.getRoomId());
        roomInnerService.updateConfigValueByRoomId(vo.getRoomId(), vo.getStatus(), RoomConfigStatusEnum.RESERVATION_STATUS.getValue());
        return true;
    }

    @Override
    public Boolean commentStatusRoom(RoomCommentStatusVO vo) {
        // 直播中禁止编辑
        prohibitEditing(vo.getRoomId());

        if (vo.getChatManagementLiveStatus() != null) {
            roomInnerService.updateConfigValueByRoomId(vo.getRoomId(), vo.getChatManagementLiveStatus(), RoomConfigStatusEnum.CHAT_MANAGEMENT_LIVE.getValue());
        }

        if (vo.getGiftLiveStatus() != null) {
            roomInnerService.updateConfigValueByRoomId(vo.getRoomId(), vo.getGiftLiveStatus(), RoomConfigStatusEnum.CHAT_MANAGEMENT_GIFT.getValue());
        }
        return true;
    }

    @Override
    public Boolean savePullStreamUrl(RoomPullStreamVO vo) {
        roomInnerService.updateByIl(RoomsEntityDTO.builder()
                .roomId(vo.getRoomId())
                .pullStreamUrl(vo.getStreamUrl())
                .updatedAt(LocalDateTime.now()).build());

        return true;
    }

    @Override
    public Boolean startPullStream(RoomPullStreamInfoVO vo) {
        // 获取房间信息
        RoomsEntity room = getRoomsEntity(vo.getRoomId());
        if (RoomAuditWhichEnum.OPEN_ROOM_AUDIT.getStatus().equals(roomAuditWitch)
                && !RoomAuditStatusEnum.APPROVED.getAuditStatus().equals(room.getAuditStatus())) {
            throw new BusinessException(BizErrorCode.COMP_ROOM_AUDIT_STATUS_ERROR);
        }
        JSONObject jsonObject = paasService.submitStartPullStream(vo.getRoomId(), room.getPullStreamUrl());
        Integer pullStreamConfigId = jsonObject.getInteger("config_id");

        roomInnerService.updateByIl(RoomsEntityDTO.builder()
                .roomId(vo.getRoomId())
                .pullStreamConfigId(pullStreamConfigId)
                .pullStreamStatus(RoomPullStreamEnum.PULL_STREAM_ENUM.start())
                .updatedAt(LocalDateTime.now()).build());

        return true;
    }

    @Override
    public Boolean endPullStream(RoomPullStreamInfoVO vo) {
        // 获取房间信息
        RoomsEntity room = getRoomsEntity(vo.getRoomId());

        paasService.submitDelPullStream(room.getPullStreamConfigId());

        roomInnerService.updateByIl(RoomsEntityDTO.builder()
                .ilId(room.getIlId())
                .pullStreamStatus(RoomPullStreamEnum.PULL_STREAM_ENUM.end())
                .updatedAt(LocalDateTime.now()).build());

        return true;
    }

    @Override
    public RoomPullStreamRespVO getPullStreamStatus(RoomPullStreamInfoVO vo) {
        // 获取房间信息
        RoomsEntity room = getRoomsEntity(vo.getRoomId());
        RoomPullStreamRespVO roomPullStreamRespVO = new RoomPullStreamRespVO();
        roomPullStreamRespVO.setRoomId(room.getRoomId());
        roomPullStreamRespVO.setPullStreamConfigId(room.getPullStreamConfigId());
        roomPullStreamRespVO.setPullStreamUrl(room.getPullStreamUrl());
        roomPullStreamRespVO.setPullStreamStatus(room.getPullStreamStatus());

        return roomPullStreamRespVO;
    }

    @Override
    public RoomBaseInfoResponseVO getRoomBaseInfo() {
        if (Objects.isNull(roomBaseInfoResponseVO)) {
            roomBaseInfoResponseVO = new RoomBaseInfoResponseVO();
        }
        return roomBaseInfoResponseVO;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean auditRoom(RoomAuditRequestVO vo) {
        Token token = TokenThreadLocal.getToken();
        Integer accountId = token.getAccountId();
        RoomsEntityDTO roomInfoById = Optional.ofNullable(roomInnerService.getRoomInfoById(vo.getIlId()))
                .orElseThrow(() -> new BusinessException(BizErrorCode.EMPTY_ROOM));
        Integer createdBy = roomInfoById.getAccountId();
        if (createdBy.equals(accountId)) {
            throw new BusinessException(BizErrorCode.BIZ_CREATE_USER_NOT_CAN_AUDIT);
        }
        if (!RoomAuditStatusEnum.CHECK_PENDING.getAuditStatus().equals(roomInfoById.getAuditStatus())) {
//            直播已经审核不能再次审核
            throw new BusinessException(BizErrorCode.BIZ_ROOM_CANNOT_REVIEW_AGAIN);
        }

        // 1 更新房间状态
        RoomsEntityDTO build = RoomsEntityDTO.builder().roomId(roomInfoById.getRoomId()).auditStatus(vo.getAuditStatus()).auditNote(vo.getAuditNote()).build();
        LocalDateTime startTime = roomInfoById.getStartTime();
        LocalDateTime now = LocalDateTime.now();
        /**
         * 1、当审核时间大于录播直播开始时间时：
         *
         * （1）未审核的录播房间也不能开播
         *
         * （2）此时审核通过：点击通过后，录制开始时间自动调整为审核时间+5分钟
         *
         * （3）此时审核驳回：直接驳回即可
         */
        if (Objects.equals(roomInfoById.getLiveType(), LiveRoomTypeEnum.FAKE_LIVE_BROADCAST.getType()) && startTime.isBefore(now) && Objects.equals(vo.getAuditStatus(), RoomAuditStatusEnum.APPROVED.getAuditStatus())) {
            startTime = now.plusMinutes(5);
            build.setStartTime(startTime);
        }
        roomInnerService.updateByIl(build);

        // 2 记录审核日志
        CreateAuditRecordDTO dto = new CreateAuditRecordDTO();
        dto.setAuditTable("rooms");
        dto.setAuditComments(vo.getAuditNote());
        dto.setAuditStatus(vo.getAuditStatus());
        dto.setAuditTableRecordId(vo.getIlId().toString());
        auditRecordService.createAuditRecord(dto);

        // 3 同步更新该房间对应的栏目时间信息
        if (vo.getAuditStatus().equals(RoomAuditStatusEnum.APPROVED.getAuditStatus())
                && roomInfoById.getColumnId() != null) {
            LambdaUpdateWrapper<LiveColumnEntity> update = Wrappers.lambdaUpdate();
            update.eq(LiveColumnEntity::getId, roomInfoById.getColumnId())
                    .set(LiveColumnEntity::getRoomAuditAt, now);

            liveColumnMapper.update(null, update);
        }
        addRecordedVideoToCache(roomInfoById, vo.getAuditStatus());
        if (vo.getAuditStatus() == 1) {
            roomColumnPush(roomInfoById);
        }
        return true;
    }

    private void roomColumnPush(RoomsEntityDTO roomsEntityDTO) {
        try {
            LambdaQueryWrapper<RoomColumnPushMsgThird> queryWrapper = Wrappers.lambdaQuery();
            queryWrapper.eq(RoomColumnPushMsgThird::getIlId, roomsEntityDTO.getIlId());
//            queryWrapper.eq(RoomColumnPushMsgThird::getLiveColumnId, roomsEntityDTO.getColumnId());
            queryWrapper.orderByDesc(RoomColumnPushMsgThird::getId);
            queryWrapper.last(" limit 1");
            RoomColumnPushMsgThird roomColumnPushMsgThird = roomColumnPushMsgThirdMapper.selectOne(queryWrapper);
            Integer columnId = roomsEntityDTO.getColumnId();
            LiveColumnEntity liveColumnEntity = liveColumnMapper.selectById(columnId);
            if (roomColumnPushMsgThird == null) {
                roomColumnPushMsgThird = new RoomColumnPushMsgThird();
                roomColumnPushMsgThird.setIlId(roomsEntityDTO.getIlId());
                roomColumnPushMsgThird.setRoomId(roomsEntityDTO.getRoomId());
                LocalDateTime startTime = roomsEntityDTO.getStartTime();
                ZonedDateTime zonedDateTime = startTime.atZone(ZoneId.systemDefault());
                roomColumnPushMsgThird.setRoomStartTime(Date.from(zonedDateTime.toInstant()));
                roomColumnPushMsgThird.setLiveColumnId(columnId);
                roomColumnPushMsgThird.setLiveColumnName(liveColumnEntity.getName());
                String redirectUlr = StrUtil.replace(h5Url, "il_id", String.valueOf(roomsEntityDTO.getIlId()));
                roomColumnPushMsgThird.setWatchUrl(redirectUlr);
                roomColumnPushMsgThirdMapper.insert(roomColumnPushMsgThird);
            }else {
                if (roomColumnPushMsgThird.getPushStatus() == 1){
                    return;
                }
                roomColumnPushMsgThird.setLiveColumnId(columnId);
                roomColumnPushMsgThird.setLiveColumnName(liveColumnEntity.getName());
                LocalDateTime startTime = roomsEntityDTO.getStartTime();
                ZonedDateTime zonedDateTime = startTime.atZone(ZoneId.systemDefault());
                roomColumnPushMsgThird.setRoomStartTime(Date.from(zonedDateTime.toInstant()));
                roomColumnPushMsgThirdMapper.updateById(roomColumnPushMsgThird);
            }
        } catch (Exception e) {
            log.error("栏目新增房间失败：{}", e);
            log.error("栏目新增房间失败, columnId={},ilId={}", roomsEntityDTO.getColumnId(), roomsEntityDTO.getIlId());
        }
    }


    @Override
    public List<AuditRecordResponseVO> roomAuditRecordList(RoomAuditRecordRequestVO vo) {
        AuditRecordListDTO dto = new AuditRecordListDTO();
        dto.setAuditTable("rooms");
        dto.setAuditTableRecordId(vo.getIlId().toString());
        return auditRecordService.getAuditRecordList(dto);
    }


    /**
     * 设置房间水印
     *
     * @param roomId 房间 id
     * @param tempId 水印模板id
     */
    private void setWatermark(String roomId, Integer tempId) {
        log.info("开始直播-设置房间{}水印", roomId);
        try {
            if (tempId == null) {
                String tempStr = roomInnerService.getDefaultWatermarkTemplateId();
                if (StringUtils.isNotEmpty(tempStr)) {
                    tempId = Integer.valueOf(tempStr);
                }
            }
            if (tempId == null || tempId.equals(0)) {
                return;
            }
            Map<String, Object> map = new HashMap<>();
            map.put("room_id", roomId);
            map.put("watermark_id", tempId);
            JSONObject jsonObject = paasService.setStreamWatermark(map);
            log.info("开始直播-设置房间{}水印结果：{}", roomId, jsonObject.toString());
            String resultId = jsonObject.getString("watermark_id");
            if (Integer.parseInt(resultId) != tempId) {
                throw new BusinessException("设置水印失败");
            }
        } catch (Exception e) {
            log.error("开始直播-设置水印失败:{}", roomId);
        }
    }

    /**
     * 构造PushStreamEntity实体
     *
     * @param reqVO 推流信息
     * @return 推流信息
     */
    private PushStreamEntity getPushStreamEntity(PushStreamReqVO reqVO) {
        PushStreamEntity pushStreamEntity = new PushStreamEntity();
        pushStreamEntity.setId(reqVO.getId());
        pushStreamEntity.setRoomId(reqVO.getRoomId());
        pushStreamEntity.setName(reqVO.getName());
        pushStreamEntity.setSkey(reqVO.getKey());
        pushStreamEntity.setType(reqVO.getType());
        pushStreamEntity.setRtmps(reqVO.getRtmps());
        LocalDateTime cronPushTime = reqVO.getCronPushTime();
        //校验定时推流时间
        if (Objects.nonNull(cronPushTime) && cronPushTime.isBefore(LocalDateTime.now())) {
            throw new BusinessException(BizErrorCode.BIZ_PUSH_STREAM_CRONTIME_ERROR);
        }
        pushStreamEntity.setCronPushTime(Objects.nonNull(cronPushTime) ? cronPushTime : null);
        pushStreamEntity.setStartTime(Objects.nonNull(cronPushTime) ? cronPushTime : null);
        pushStreamEntity.setEndTime(Objects.nonNull(cronPushTime) ? cronPushTime.plusDays(1) : null);
        pushStreamEntity.setStatus(0);
        return pushStreamEntity;

    }

    /**
     * 校验List中的bean
     *
     * @param pushStreamReqVos
     */
    private void validParam(List<PushStreamReqVO> pushStreamReqVos) {
        String roomId = pushStreamReqVos.get(0).getRoomId();
        Integer total = pushStreamMapper.selectCount(Wrappers.<PushStreamEntity>lambdaQuery().eq(PushStreamEntity::getRoomId, roomId));
        //1.校验数量
        if (total + pushStreamReqVos.size() > MAX_PUSH) {
            throw new BusinessException(BizErrorCode.BIZ_PUSH_STREAM_LIMIT);
        }
        pushStreamReqVos.forEach(ps -> {
            roomInnerService.checkRoomId(ps.getRoomId());
            //校验type
            if (Objects.isNull(ps.getType())) {
                throw new BusinessException(BizErrorCode.EMPTY_PUSH_STREAM_TYPE);
            }
            //校验流地址是否为空
            if (StringUtils.isEmpty(ps.getRtmps())) {
                throw new BusinessException(BizErrorCode.EMPTY_PUSH_STREAM_ADDRESS);
            }
            extracted(ps);
            //校验推流地址是否重复
            List<PushStreamEntity> pushStreamEntityList = pushStreamMapper.selectList(Wrappers.<PushStreamEntity>lambdaQuery()
                    .eq(PushStreamEntity::getRoomId, ps.getRoomId())
                    .eq(PushStreamEntity::getRtmps, ps.getRtmps())
                    .eq(PushStreamEntity::getSkey, ps.getKey()));
            if (!pushStreamEntityList.isEmpty()) {
                throw new BusinessException(BizErrorCode.BIZ_REPEAT_CREATE);
            }
        });

    }

    /**
     * 校验推流地址是否合法
     *
     * @param ps 流信息
     */
    private void extracted(PushStreamReqVO ps) {
        //校验流地址是否符合规范
        if (!ps.getRtmps().startsWith("rtmp://") && !ps.getRtmps().startsWith("rtmps://")) {
            throw new BusinessException(BizErrorCode.BIZ_PUSH_STREAM_URL_ERROR);
        }
    }

    private String coverImageUpload(String imgBase64) {
        String url = "";
        if (StringUtils.isEmpty(imgBase64)) {
            return url;
        }
        try {

            imgBase64 = imgBase64.replace("\\+", "%2B");
            String image = URLDecoder.decode(imgBase64, CommonConstant.UTF8);
            byte[] bytes = Base64Utils.decodeFromString(image);
            String type = "image/jpeg";
            String name = IdUtil.fastSimpleUUID() + ".jpg";
            ByteArrayInputStream stream = new ByteArrayInputStream(bytes);
            url = ossService.upload(stream, name, type);
        } catch (Exception e) {
            log.error("上传房间背景图失败", e);
        }
        return url;
    }

    /**
     * 获取房间与主播关联信息
     *
     * @param ilId 房间id
     * @return 房间与主播关联信息
     */
    private List<ThirdUserVO> getAnchorRoomInfo(Integer ilId) {
        List<ThirdUserVO> result;
        LambdaQueryWrapper<AnchorRoomRelationEntity> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(AnchorRoomRelationEntity::getIlId, ilId);
        List<AnchorRoomRelationEntity> list = anchorRoomRelationService.list(queryWrapper);
        result = Optional.ofNullable(list.stream().map(data -> {
            ThirdUserVO temp = new ThirdUserVO();
            temp.setName(data.getUserName());
            temp.setUserCode(data.getUserCode());
            temp.setUserType(data.getUserType());
            temp.setOrgCode(data.getOrgCode());
            temp.setOrgName(data.getOrgName());
            temp.setAdvisorLicenseNo(data.getAdvisorLicenseNo());
            return temp;
        }).collect(Collectors.toList())).orElse(new ArrayList<>());
        return result;
    }

}
